MQL5プログラミングベージックス:リスト

Denis Kirichenko | 23 12月, 2015

はじめに

MQL言語の新バージョンは、自動トレーディングシステムの開発者に複雑なタスクを実装するための効果的なツールを提供しています。その言語のプログラミングの機能がかなり拡大されたという事実を否定することは誰もできません。MQL5 OOP機能だけでもかなりの価値があります。また、標準ライブラリも紹介せずにはいられません。エラーコード番号358によって判断すると、クラステンプレートはすぐにサポートされるでしょう。

この記事では、データ型やデータセットを記載するテーマの続きであり、拡大判でもある内容を取り上げます。ここでは、MQL5.communityウェブサイトにて公開された記事を参照したいと思います。配列を扱うロジックや原則におけるとても詳細な記述は、Dmitry Fedoseev (Integer) の "MQL5 Programming Basics: Arrays"という記事にて紹介されています。

なので、今回はリスト、より正確に言うと、接続された線形リストに焦点を当てたいと思います。リストの構造、意味やロジックから始めます。そのあと、標準ライブラリにて使用可能な関連するツールを紹介します。MQL5を扱う際に、いかにリストが使用されているかの例を記載しています。

  1. リストとノードのコンセプト:理論
  2. リストとノードのコンセプト:プログラミング
  3. MQL5標準ライブラリのリスト
  4. MQL5のリストを用いた例


1. リストとノードのコンセプト:理論

開発者にとってリストとは何で、何をすればよいのでしょうか?この用語の定義について、公的な情報源であるWikipediaを参照します。

計算機科学にて、リストは、値の有限的な収集を実装する抽象的なデータ型で、同じ値が一度以上発生する場合があります。リストのインスタンスは、タプル - 数学的な概念の有限なシーケンスのコンピューターでの表現です。リストの値のインスタンスは、通常アイテムやエントリ、リストの要素と呼ばれます;もし同じ値が複数回発生すれば、それぞれが個別のアイテムとしてみなされます。

その用語「リスト」は、抽象的なリスト、特に接続されたリストを実装するために使用される複数の具体的なデータ構造のために使用されます。

この定義がかなり学者的であると感じるかと思います。

この記事において、この定義の最後の行にさらに興味を持ってもらいます。それでは見てみましょう:

計算機科学では、接続されたリストは、ノードからなるベーシックで動的なデータストラクチャーで、それぞれのノードは データと、リストの次か前のノードへの1つか2つのリファレンス を持ちます。[1]その従来の配列上の接続されたリストの基本的な利点は、構造的な柔軟性です;接続されたリストのアイテムは、コンピューターメモリーのデータ要素とマッチする必要はなく、リストアイテムの内部リンクは常にリストトラバーサルのためにに維持されます。

ステップごとに一つずつ見ていきましょう。

計算機科学では、リストそれ自体はいくつかのデータ型です。すでに作成しています。それは、複数のデータ型を含むので、むしろ統合的なデータ型と言えます。リストは幾分配列に似ています。もし一つのデータ型の配列が新しいデータ型として分類されれば、それはリストになるでしょう。しかし、完全にそういうわけではありません。

リストの基本的な利点は、必要であればリストのどの部分からでもノードの挿入や削除が可能であるという点です。リストは動的配列に似ています。ただ全くArrayResize()関数を使う必要はありません。

メモリー要素の順序について言うと、リストノードは保存されず、配列要素が近接のメモリ領域に保存されるような同じ方法で保存される必要がありません。

だいたいそのような感じです。さらに詳しく見ていきましょう。


1.1単独で接続されたリストのノード

リストはアイテムの代わりのノードの保存を可能にします。ノードは、二つの部分からなるデータ型です。

最初の部分はデータフィールドで、二つ目の部分はその他のノードとのリンクのために使用されます(図1)最初のノードは、「ヘッド」と呼ばれ、最後のノードは「テール」と呼ばれます。テールリンクフィールドは、NULLリファレンスを所持します。基本的にリストのさらなるノードの欠如を示すために使用されます。その他の特別なソースは、ヘッドのあとのリストの残りを「テール」とします。

図 1 単独で接続されたリストのノード

図 1 単独で接続されたリストのノード

単独で接続されたリストノードとは別に異なる種類のノードがあります。二重で接続されたリストのノードは、おそらく最も共通のものです。


1.2二重で接続されたリストのノード

二重で接続されたリストのニーズを満たすノードが必要です。以前のノードを指す別のノードを所持するので、以前の種類とは異なります。そして、そのリストのヘッドのノードは、NULLリファレンスを所持します。そのノードを所持するリストの構造を表示しているダイアグラムにて、以前のノードを指すリンクが赤色の配列として表示されています。

図2 二重で接続されたリストのノード

図2 二重で接続されたリストの ノード

したがって、二重で接続されたリストのノードの機能は、単独で接続されたリストのノードのそれと似ています。以前のノードへの一つ以上のリンクを扱う必要があります。


1.3円で二重に接続されたリストのノード

上記のノードが非線形リストにて使用されるケースがあります。そして、記事では主に線形リストを紹介しますが、円のリストの例も紹介します。

図3 円で二重に接続されたリストのノード

図3 円で二重に接続されたリストのノード

円の二重で接続されたリストのダイアグラム(図3)は、二つのリンクフィールドを持つノードが単純に円で接続されています。これにはオレンジの矢印が用いられます。そのため、ヘッドノードは、テールに接続されます。そして、テールノードのリンクフィールドは、ヘッドを指し空白ではありません。


1.4 主なリスト処理

以前述べられた通り、すべてのリストの処理は3つのグループに分けられます:

  1. 追加(リストへの新しいノードの追加);
  2. 削除(リストからのノードの削除)
  3. チェック(ノードのデータ)

追加メソッドは以下を含みます;

削除の処理に関する限り、仮想的に追加グループの一致する処理をコピーしmさう。

デストラクターは、リストの処理を終了するだけではなく、適切にすべての要素を削除する役割を担います。

様々なチェック処理の3番目のグループは、リストのノードやノード値にアクセスします。

そのグループに追加して、4番目のサービスグループを分けたいと思います。以前のグループ群に以下を提供します;

以上です。開発者は当然必要であればいつでもリストクラスの機能を拡大できます。


2. リストとノードのコンセプト:プログラミング

このパートでは、直接ノードとリストのプログラミングに進みたいと思います。コードの例は必要であれば提供されます。


2.1単独で接続されたリストのノード

単独で接続されたリストのニーズを満たすノードクラス(図4)の土台を築きましょう。"How to Develop an Expert Advisor using UML Tools(UMLツールを用いてどのようにエキスパートアドバイザーを開発するか)" という記事にてクラスのダイアグラムの観念を学びましょう。. (CTradeExpertクラスのUMLモデルを図5にてご覧ください。)

CiSingleNodeクラスモデル

図4 CiSingleNode クラスモデル

それではコードを見てみましょう。Art Friedmanやその他の著者による本にて提供されている例に基づいています。"C/C++ Annotated Archives".

//+------------------------------------------------------------------+
//|                     CiSingleNode class                           |
//+------------------------------------------------------------------+
class CiSingleNode
  {
protected:
   int               m_val;   // data
   CiSingleNode     *m_next;  // pointer to the next node
public:
   void              CiSingleNode(void);                             // default constructor
   void              CiSingleNode(int _node_val);                    // parameterized constructor
   void             ~CiSingleNode(void);                             // destructor
   void              SetVal(int _node_val);                          // set-method for data
   void              SetNextNode(CiSingleNode *_ptr_next);           // set-method for the next node
   virtual void      SetPrevNode(CiSingleNode *_ptr_prev){};         // set-method for the previous node
   virtual CiSingleNode *GetPrevNode(void) const {return NULL;};     // get-method for the previous node
   CiSingleNode     *GetNextNode(void) const;                        // get-method for the next node 
   int               GetVal(void){TRACE_CALL(_t_flag) return m_val;} // get-method for data 
  };

CiSingleNodeクラスの各メソッドについて説明はしません。CiSingleNode.mqhという添付ファイルにてそれらを詳しくみることができます。しかし、面白いニュアンスに注目してみたいと思います。そのクラスは前のノードを扱う仮想メソッドを持っています。実際それらは偽物で、将来の子クラスのための多相性のためにあります。

そのコードは、各メソッドの呼び出しのために必要なTRACE_CALL(f)前処理プログラムを用います。

#define TRACE_CALL(f) if(f) Print("Calling: "+__FUNCSIG__);

CiSingleNodeクラスのみが使用できる状態で、単独で接続されるリストを作成します。コードの例を紹介します。

//=========== Example 1 (processing the CiSingleNode type )
 
   CiSingleNode *p_sNodes[3];                             // #1
   p_sNodes[0]=NULL;
   srand(GetTickCount()); // initialize a random number generator
//--- create nodes
   for(int i=0;i<ArraySize(p_sNodes);i++)
      p_sNodes[i]=new CiSingleNode(rand());               // #2
//--- links
   for(int j=0;j<(ArraySize(p_sNodes)-1);j++)
      p_sNodes[j].SetNextNode(p_sNodes[j+1]);             // #3
//--- check values
   for(int i=0;i<ArraySize(p_sNodes);i++)
     {
      int val=p_sNodes[i].GetVal();                       // #4
      Print("Node #"+IntegerToString(i+1)+                // #5
            " value = "+IntegerToString(val));
     }
//--- check next-nodes
   for(int j=0;j<(ArraySize(p_sNodes)-1);j++)
     {
      CiSingleNode *p_sNode_next=p_sNodes[j].GetNextNode(); // #9
      int snode_next_val=p_sNode_next.GetVal();             // #10
      Print("Next-Node #"+IntegerToString(j+1)+             // #11
            " value = "+IntegerToString(snode_next_val));
     }
//--- delete nodes
   for(int i=0;i<ArraySize(p_sNodes);i++)
      delete p_sNodes[i];                                   // #12 

string #1では、CiSingleNode 型のオブジェクトへのポインター配列を宣言します。string #2では、その配列は作成されたポインターにて格納されます。それぞれのノードのデータのために、rand() 関数を用いて、0から32767までの範囲のランダムな数字.を取得します。 そのノードは、次のstring #3に接続されています。strings #4-5では、ノードの値をチェックし、strings #9-11では、リンクのパフォーマンスをチェックします。そのポインターは、string #12にて削除されます。

これはログに表示されていた点です。

DH      0       23:23:10        test_nodes (EURUSD,H4)  Node #1 value = 3335
KP      0       23:23:10        test_nodes (EURUSD,H4)  Node #2 value = 21584
GI      0       23:23:10        test_nodes (EURUSD,H4)  Node #3 value = 917
HQ      0       23:23:10        test_nodes (EURUSD,H4)  Next-Node #1 value = 21584
HI      0       23:23:10        test_nodes (EURUSD,H4)  Next-Node #2 value = 917

結果として作成されるノードの構造は、以下に示されています。(図5).

図5 CiSingleNode *p_sNodes[3]配列におけるノード間のリンク

図5 CiSingleNode *p_sNodes[3]配列におけるノード間のリンク

二重で接続されたリストのノードに進みましょう。

2.2二重で接続されたリストのノード

まずは、二重で接続されたリストが二つのポインター;次へのノードポインター、一つ前へのノードポイター、を持つため、異なっていまうs。すなわち、次へのノードリンクに加えて、単独で接続されたリストノードへのノードのポインターを加える必要があります。

このため、クラス関係として継承を使用することを勧めます。二重で接続されたノードにおけるクラスモデルは、以下の通りです(図 6).

CDoubleNode クラスモデル

図6 CDoubleNode クラスモデル

それでは、コードを見てみましょう。

//+------------------------------------------------------------------+
//|                    CDoubleNode class                             |
//+------------------------------------------------------------------+
class CDoubleNode : public CiSingleNode
  {
protected:
   CiSingleNode     *m_prev;  // pointer to the previous node 

public:
   void              CDoubleNode(void);                     // default constructor
   void              CDoubleNode(int node_val);             // parameterized constructor
   void             ~CDoubleNode(void){TRACE_CALL(_t_flag)};// destructor
   virtual void      SetPrevNode(CiSingleNode *_ptr_prev);  // set-method for the previous node
   virtual CiSingleNode *GetPrevNode(void) const;           // get-method for the previous node CDoubleNode
  };

いくつかの追加メソッドがありますーそれらは仮想のもので、一つ前のノードを扱う部分に関連しています。そのクラスの完全な記述はCDoubleNode.mqhにて提供されています。

CDoubleNode クラスに基づく二重で接続されたリストを作成しましょう。コードの例を紹介します。

//=========== Example 2 (processing the CDoubleNode type)

   CiSingleNode *p_dNodes[3];                             // #1
   p_dNodes[0]=NULL;
   srand(GetTickCount()); // initialize a random number generator
//--- create nodes
   for(int i=0;i<ArraySize(p_dNodes);i++)
      p_dNodes[i]=new CDoubleNode(rand());                // #2
//--- links
   for(int j=0;j<(ArraySize(p_dNodes)-1);j++)
     {
      p_dNodes[j].SetNextNode(p_dNodes[j+1]);             // #3
      p_dNodes[j+1].SetPrevNode(p_dNodes[j]);             // #4
     }
//--- check values
   for(int i=0;i<ArraySize(p_dNodes);i++)
     {
      int val=p_dNodes[i].GetVal();                       // #4
      Print("Node #"+IntegerToString(i+1)+                // #5
            " value = "+IntegerToString(val));
     }
//--- check next-nodes
   for(int j=0;j<(ArraySize(p_dNodes)-1);j++)
     {
      CiSingleNode *p_sNode_next=p_dNodes[j].GetNextNode(); // #9
      int snode_next_val=p_sNode_next.GetVal();             // #10
      Print("Next-Node #"+IntegerToString(j+1)+             // #11
            " value = "+IntegerToString(snode_next_val));
     }
//--- check prev-nodes
   for(int j=0;j<(ArraySize(p_dNodes)-1);j++)
     {
      CiSingleNode *p_sNode_prev=p_dNodes[j+1].GetPrevNode(); // #12
      int snode_prev_val=p_sNode_prev.GetVal();               // #13
      Print("Prev-Node #"+IntegerToString(j+2)+               // #14
            " value = "+IntegerToString(snode_prev_val));
     }
//--- delete nodes
   for(int i=0;i<ArraySize(p_dNodes);i++)
      delete p_dNodes[i];                                     // #15 

原則、これは単独で接続されるリストの作成に似ていますが、いくつか特徴的な点があります。ポインターの配列、p_dNodes[]がstring #1にてどのように宣言されているか注意してください。ポインターの種類はベースクラスと一致して設定されます。string #2の多相性の原則は、将来それらを認識させるのに役に立ちます。一つ前のノードがstings #12-14にてチェックされます。

以下の情報がログに追加されました。

GJ      0       16:28:12        test_nodes (EURUSD,H4)  Node #1 value = 17543
IQ      0       16:28:12        test_nodes (EURUSD,H4)  Node #2 value = 1185
KK      0       16:28:12        test_nodes (EURUSD,H4)  Node #3 value = 23216
DS      0       16:28:12        test_nodes (EURUSD,H4)  Next-Node #1 value = 1185
NH      0       16:28:12        test_nodes (EURUSD,H4)  Next-Node #2 value = 23216
FR      0       16:28:12        test_nodes (EURUSD,H4)  Prev-Node #2 value = 17543
LI      0       16:28:12        test_nodes (EURUSD,H4)  Prev-Node #3 value = 1185

そのノード構造は以下の通りです(図7);

図7 CDoubleNode *p_sNodes[3]配列でのノード間のリンク

図7 CDoubleNode *p_sNodes[3]配列でのノード間のリンク

それでは、展開され二重で接続されたリストを作成する際に必要なノードを紹介します。

2.3展開され二重に接続されたリストのノード

すべての配列に帰することができるデータメンバーを含むノードを考えてみましょう。それは配列全体を含んでいます。展開されたリストを作成するために使用されるノードこのノードは二重で接続されたリストの標準のノードと完全に同じなので、ここでは例は提供しません。唯一の違いは、「データ」の属性がすべての配列をカプセル化することです。

継承を再度使用したいと思います。CDoubleNodeクラスは、展開され二重で接続されるリストのノードにおけるベースクラスとして機能します。そして、そのリストのノードにおけるクラスモデルは以下の通りです(図8)

CiUnrollDoubleNodeクラスモデル

図8 CiUnrollDoubleNodeクラスモデル

CiUnrollDoubleNodeクラスは、以下のコードを用いて定義されます。

//+------------------------------------------------------------------+
//|                    CiUnrollDoubleNode class                      |
//+------------------------------------------------------------------+
class CiUnrollDoubleNode : public CDoubleNode
  {
private:
   int               m_arr_val[]; // data array

public:
   void              CiUnrollDoubleNode(void);               // default constructor 
   void              CiUnrollDoubleNode(int &_node_arr[]);   // parameterized constructor
   void             ~CiUnrollDoubleNode(void);               // destructor
   bool              GetArrVal(int &_dest_arr_val[])const;   // get-method for data array
   bool              SetArrVal(const int &_node_arr_val[]);  // set-method for data array
  };

CiUnrollDoubleNode.mqhの各メソッドをより詳しく見てみましょう。

例としてパラメーター化されたコンストラクターを紹介します。

//+------------------------------------------------------------------+
//|                   Parameterized constructor                      |
//+------------------------------------------------------------------+
void CiUnrollDoubleNode::CiUnrollDoubleNode(int &_node_arr[])
   : CDoubleNode(ArraySize(_node_arr))
  {
   ArrayCopy(this.m_arr_val,_node_arr);
   TRACE_CALL(_t_flag)
  }

初期化リストを用いて、データメンバーthis.m_valの一次元配列のサイズを入力します。

その後、「手動で」展開され二重で接続されたリストを作成し、その中でリンクをチェックします。

//=========== Example 3 (processing the CiUnrollDoubleNode type)

//--- data arrays
   int arr1[],arr2[],arr3[];                                  // #1
   int arr_size=15;
   ArrayResize(arr1,arr_size);
   ArrayResize(arr2,arr_size);
   ArrayResize(arr3,arr_size);
   srand(GetTickCount()); // initialize a random number generator
   
//--- fill the arrays with pseudorandom integers   
   for(int i=0;i<arr_size;i++)
     {
      arr1[i]=rand();                                         // #2
      arr2[i]=rand();
      arr3[i]=rand();
     }
//--- create nodes
   CiUnrollDoubleNode *p_udNodes[3];                          // #3
   p_udNodes[0]=new CiUnrollDoubleNode(arr1);
   p_udNodes[1]=new CiUnrollDoubleNode(arr2);
   p_udNodes[2]=new CiUnrollDoubleNode(arr3);
//--- links
   for(int j=0;j<(ArraySize(p_udNodes)-1);j++)
     {
      p_udNodes[j].SetNextNode(p_udNodes[j+1]);               // #4
      p_udNodes[j+1].SetPrevNode(p_udNodes[j]);               // #5
     }
//--- check values
   for(int i=0;i<ArraySize(p_udNodes);i++)
     {
      int val=p_udNodes[i].GetVal();                          // #6
      Print("Node #"+IntegerToString(i+1)+                    // #7
            " value = "+IntegerToString(val));
     }
//--- check array values
   for(int i=0;i<ArraySize(p_udNodes);i++)
     {
      int t_arr[]; // destination array 
      bool isCopied=p_udNodes[i].GetArrVal(t_arr);            // #8
      if(isCopied)
        {
         string arr_str=NULL;
         for(int n=0;n<ArraySize(t_arr);n++)
            arr_str+=IntegerToString(t_arr[n])+", ";
         int end_of_string=StringLen(arr_str);
         arr_str=StringSubstr(arr_str,0,end_of_string-2);
         Print("Node #"+IntegerToString(i+1)+                 // #9
               " array values = "+arr_str);
        }
     }
//--- check next-nodes
   for(int j=0;j<(ArraySize(p_udNodes)-1);j++)
     {
      int t_arr[]; // destination array 
      CiUnrollDoubleNode *p_udNode_next=p_udNodes[j].GetNextNode(); // #10
      bool isCopied=p_udNode_next.GetArrVal(t_arr);
      if(isCopied)
        {
         string arr_str=NULL;
         for(int n=0;n<ArraySize(t_arr);n++)
            arr_str+=IntegerToString(t_arr[n])+", ";
         int end_of_string=StringLen(arr_str);
         arr_str=StringSubstr(arr_str,0,end_of_string-2);
         Print("Next-Node #"+IntegerToString(j+1)+
               " array values = "+arr_str);
        }
     }
//--- check prev-nodes
   for(int j=0;j<(ArraySize(p_udNodes)-1);j++)
     {
      int t_arr[]; // destination array 
      CiUnrollDoubleNode *p_udNode_prev=p_udNodes[j+1].GetPrevNode(); // #11
      bool isCopied=p_udNode_prev.GetArrVal(t_arr);
      if(isCopied)
        {
         string arr_str=NULL;
         for(int n=0;n<ArraySize(t_arr);n++)
            arr_str+=IntegerToString(t_arr[n])+", ";
         int end_of_string=StringLen(arr_str);
         arr_str=StringSubstr(arr_str,0,end_of_string-2);
         Print("Prev-Node #"+IntegerToString(j+2)+
               " array values = "+arr_str);
        }
     }
//--- delete nodes
   for(int i=0;i<ArraySize(p_udNodes);i++)
      delete p_udNodes[i];                                            // #12
  }

コードの量はわずかにより大きくなりました。これはそれぞれのノードに配列を作成し、格納する必要があるという事実が関係しています。

データ配列の取り扱いは、string #1にて始まります。それは基本的に以前ノードで持っていたものに似ています。配列全体における各ノードのデータ値を表示する必要があります(例、string #9)

こちらが表示されるものです:

IN      0       00:09:13        test_nodes (EURUSD.m,H4)        Node #1 value = 15
NF      0       00:09:13        test_nodes (EURUSD.m,H4)        Node #2 value = 15
CI      0       00:09:13        test_nodes (EURUSD.m,H4)        Node #3 value = 15
FQ      0       00:09:13        test_nodes (EURUSD.m,H4)        Node #1 array values = 31784, 4837, 25797, 29079, 4223, 27234, 2155, 32351, 12010, 10353, 10391, 22245, 27895, 3918, 12069
EG      0       00:09:13        test_nodes (EURUSD.m,H4)        Node #2 array values = 1809, 18553, 23224, 20208, 10191, 4833, 25959, 2761, 7291, 23254, 29865, 23938, 7585, 20880, 25756
MK      0       00:09:13        test_nodes (EURUSD.m,H4)        Node #3 array values = 18100, 26358, 31020, 23881, 11256, 24798, 31481, 14567, 13032, 4701, 21665, 1434, 1622, 16377, 25778
RP      0       00:09:13        test_nodes (EURUSD.m,H4)        Next-Node #1 array values = 1809, 18553, 23224, 20208, 10191, 4833, 25959, 2761, 7291, 23254, 29865, 23938, 7585, 20880, 25756
JD      0       00:09:13        test_nodes (EURUSD.m,H4)        Next-Node #2 array values = 18100, 26358, 31020, 23881, 11256, 24798, 31481, 14567, 13032, 4701, 21665, 1434, 1622, 16377, 25778
EH      0       00:09:13        test_nodes (EURUSD.m,H4)        Prev-Node #2 array values = 31784, 4837, 25797, 29079, 4223, 27234, 2155, 32351, 12010, 10353, 10391, 22245, 27895, 3918, 12069
NN      0       00:09:13        test_nodes (EURUSD.m,H4)        Prev-Node #3 array values = 1809, 18553, 23224, 20208, 10191, 4833, 25959, 2761, 7291, 23254, 29865, 23938, 7585, 20880, 25756

ノードを扱う部分の下に線を引き、異なるリストのクラス定義に直接進む必要があります。例 1-3は、test_nodes.mq5スクリプトにて見つけることができます。


2.4 単独で接続されたリスト

単独で接続されたリストのクラスモデルをリスト処理グループ(図9)から作成しましょう。

CiSingleList クラスモデル

図9 CiSingleList クラスモデル

CiSingleList クラスは、CiSingleNode 型のノードを使用することがわかると思います。クラス間の関係の種類といえば、以下のことが言えます;

  1. CiSingleListクラスは、 CiSingleNodeクラスを所持しています(構成)
  2. CiSingleListクラスは、 CiSingleNodeクラスメソッドを使用します(依存)

上記の関係の例は、図10にて提供されています。

図10 CiSingleListクラスとCiSingleNodクラス間の関係の種類

図10 CiSingleListクラスとCiSingleNodクラス間の関係の種類

新しいクラスを作成しましょう - CiSingleList. この記事にて使用される他のすべてのリストクラスは、このクラスに基づきます。そのため、それは「リッチ」な構造です。

//+------------------------------------------------------------------+
//|                     CiSingleList class                           |
//+------------------------------------------------------------------+
class CiSingleList
  {
protected:
   CiSingleNode     *m_head;    // head
   CiSingleNode     *m_tail;    // tail
   uint              m_size;    // number of nodes in the list
public:
   //--- constructor and destructor
   void              CiSingleList();                              // default constructor 
   void              CiSingleList(int _node_val);                 // parameterized constructor 
   void             ~CiSingleList();                              // destructor                

   //--- adding nodes   
   void              AddFront(int _node_val);                         // add a new node to the beginning of the list
   void              AddRear(int _node_val);                          // add a new node to the end of the list   
   virtual void      AddFront(int &_node_arr[]){TRACE_CALL(_t_flag)}; // add a new node to the beginning of the list
   virtual void      AddRear(int &_node_arr[]){TRACE_CALL(_t_flag)};  // add a new node to the end of the list
   //--- deleting nodes     
   int               RemoveFront(void);                           // delete the head node       
   int               RemoveRear(void);                            // delete the node from the end of the list
   void              DeleteNodeByIndex(const uint _idx);          // delete the ith node from the list

   //--- checking   
   virtual bool      Find(const int _node_val) const;             // find the required value    
   bool              IsEmpty(void) const;                         // check the list for being empty
   virtual int       GetValByIndex(const uint _idx) const;        // value of the ith node in the list
   virtual CiSingleNode *GetNodeByIndex(const uint _idx) const;   // get the ith node in the list
   virtual bool      SetNodeByIndex(CiSingleNode *_new_node,const uint _idx); // insert the new ith node in the list
   CiSingleNode     *GetHeadNode(void) const;                     // get the head node
   CiSingleNode     *GetTailNode(void) const;                     // get the tail node
   virtual uint      Size(void) const;                            // list size
   //--- service
   virtual void      PrintList(string _caption=NULL);             // print the list
   virtual bool      CopyByValue(const CiSingleList &_sList);     // copy the list by values
   virtual void      BubbleSort(void);                            // bubble sorting
   //---templates
   template<typename dPointer>
   bool              CheckDynamicPointer(dPointer &_p);           // template for checking a dynamic pointer
   template<typename dPointer>
   bool              DeleteDynamicPointer(dPointer &_p);          // template for deleting a dynamic pointer

protected:
   void              operator=(const CiSingleList &_sList) const; // assignment operator
   void              CiSingleList(const CiSingleList &_sList);    // copy constructor
   virtual bool      AddToEmpty(int _node_val);                   // add a new node to an empty list
   virtual void      addFront(int _node_val);                     // add a new "native" node to the beginning of the list
   virtual void      addRear(int _node_val);                      // add a new "native" node to the end of the list
   virtual int       removeFront(void);                           // delete the "native" head node
   virtual int       removeRear(void);                            // delete the "native" node from the end of the list
   virtual void      deleteNodeByIndex(const uint _idx);          // delete the "native" ith node from the list
   virtual CiSingleNode *newNode(int _val);                       // new "native" node
   virtual void      CalcSize(void) const;                        // calculate the list size
  };

そのクラスメソッドの完全な定義は、CiSingleList.mqhにて提供されています。

このクラスを開発し始めた時は、3つのデータメンバーといくつかのメソッドしかありませんでした。しかし、このクラスがその他のクラスの基礎として機能し、複数の仮想メンバー関数を追加しなければなりませんでした。詳しくはこのメソッドについて記載しません。この単独で接続されるリストクラスの使用例は、test_sList.mq5スクリプトにてご覧になれます。

トレースフラッグなしで稼働される場合、以下のエントリがログに表示されます:

KG      0       12:58:32        test_sList (EURUSD,H1)  =======List #1=======
PF      0       12:58:32        test_sList (EURUSD,H1)  Node #1, val=14 
RL      0       12:58:32        test_sList (EURUSD,H1)  Node #2, val=666 
MD      0       12:58:32        test_sList (EURUSD,H1)  Node #3, val=13 
DM      0       12:58:32        test_sList (EURUSD,H1)  Node #4, val=11 
QE      0       12:58:32        test_sList (EURUSD,H1)  
KN      0       12:58:32        test_sList (EURUSD,H1)  
LR      0       12:58:32        test_sList (EURUSD,H1)  =======List #2=======
RE      0       12:58:32        test_sList (EURUSD,H1)  Node #1, val=14 
DQ      0       12:58:32        test_sList (EURUSD,H1)  Node #2, val=666 
GK      0       12:58:32        test_sList (EURUSD,H1)  Node #3, val=13 
FP      0       12:58:32        test_sList (EURUSD,H1)  Node #4, val=11 
KF      0       12:58:32        test_sList (EURUSD,H1)  
MK      0       12:58:32        test_sList (EURUSD,H1)  
PR      0       12:58:32        test_sList (EURUSD,H1)  =======renewed List #2=======
GK      0       12:58:32        test_sList (EURUSD,H1)  Node #1, val=11 
JP      0       12:58:32        test_sList (EURUSD,H1)  Node #2, val=13 
JI      0       12:58:32        test_sList (EURUSD,H1)  Node #3, val=14 
CF      0       12:58:32        test_sList (EURUSD,H1)  Node #4, val=34 
QL      0       12:58:32        test_sList (EURUSD,H1)  Node #5, val=35 
OE      0       12:58:32        test_sList (EURUSD,H1)  Node #6, val=36 
MR      0       12:58:32        test_sList (EURUSD,H1)  Node #7, val=37 
KK      0       12:58:32        test_sList (EURUSD,H1)  Node #8, val=38 
MS      0       12:58:32        test_sList (EURUSD,H1)  Node #9, val=666 
OF      0       12:58:32        test_sList (EURUSD,H1)  
QK      0       12:58:32        test_sList (EURUSD,H1)  

そのスクリプトは、二つの単独で接続されたリストに格納hし、2番目のリストをソートします。

2.5 二重で接続されたリスト

以前の種類のリストに基づいた、二重で接続されたリストの作成を行いましょう。二重で接続されたリストのクラスモデルの例は、図11にて提供されています:

CDoubleList クラスモデル

図11 CDoubleList クラスモデル

子孫クラスは、より少ないメソッドを持ち、データメンバーはともにありません。以下は、CDoubleListクラスの定義です。

//+------------------------------------------------------------------+
//|                      CDoubleList class                           |
//+------------------------------------------------------------------+
class CDoubleList : public CiSingleList
  {
public:
   void              CDoubleList(void);                  // default constructor    
   void              CDoubleList(int _node_val);         // parameterized constructor   
   void             ~CDoubleList(void){};                // destructor                  
   virtual bool      SetNodeByIndex(CiSingleNode *_new_node,const uint _idx); // insert the new ith node in the list  

protected:
   virtual bool      AddToEmpty(int _node_val);          // add a node to an empty list
   virtual void      addFront(int _node_val);            // add a new "native" node to the beginning of the list
   virtual void      addRear(int _node_val);             // add a new "native" node to the end of the list 
   virtual int       removeFront(void);                  // delete the "native" head node
   virtual int       removeRear(void);                   // delete the "native" tail node
   virtual void      deleteNodeByIndex(const uint _idx); // delete the "native" ith node from the list
   virtual CiSingleNode *newNode(int _node_val);         // new "native" node
  };

CDoubleList クラスメソッドの完全な詳細は、 CDoubleList.mqhにて提供されています。

一般的に言うと、仮想メソッドは、単独で接続されたリストにて存在しない以前のノードへのポインターのニーズを満たすためのみに使用されます。

CDoubleList種類のリストの使用例は、test_dList.mq5スクリプトにてご覧になれます。それは、このリストタイプに関連したすべての共通のリスト処理を示しています。そのスクリプトコードは、特定のStringを所持しています;

CiSingleNode *_new_node=new CDoubleNode(666);     // create a new node of CDoubleNode type

そのような構築は基礎クラスのポインターが子孫クラスのオブジェクトを記述している場合、需要可能なので、エラーはありません。これは、継承の一つの利点です。

MQL5では、С++と同様に、その基礎クラスへのポインターは、基礎クラスから派生したサブクラスのオブジェクトを指します。しかし、その逆は無効です。

もし以下の通りにstringを記述すると;

CDoubleNode*_new_node=new CiSingleNode(666);

コンパイラは、エラーや警告を表示しませんが、そのプログラムは、このStringに達するまで稼働します。この場合、ポインターにひょり参照されている異なった型のキャストに関するメッセージをご覧になれます。最新のバインドの仕組みはプログラムが稼働した際にやっと動くので、注意深くクラス間の関係の階層構造を考えなければなりません。

そのスクリプトを稼働させた後、ログは以下のエントリを所持します;

DN      0       13:10:57        test_dList (EURUSD,H1)  =======List #1=======
GO      0       13:10:57        test_dList (EURUSD,H1)  Node #1, val=14 
IE      0       13:10:57        test_dList (EURUSD,H1)  Node #2, val=666 
FM      0       13:10:57        test_dList (EURUSD,H1)  Node #3, val=13 
KD      0       13:10:57        test_dList (EURUSD,H1)  Node #4, val=11 
JL      0       13:10:57        test_dList (EURUSD,H1)  
DG      0       13:10:57        test_dList (EURUSD,H1)  
CK      0       13:10:57        test_dList (EURUSD,H1)  =======List #2=======
IL      0       13:10:57        test_dList (EURUSD,H1)  Node #1, val=14 
KH      0       13:10:57        test_dList (EURUSD,H1)  Node #2, val=666 
PR      0       13:10:57        test_dList (EURUSD,H1)  Node #3, val=13 
MI      0       13:10:57        test_dList (EURUSD,H1)  Node #4, val=11 
DO      0       13:10:57        test_dList (EURUSD,H1)  
FR      0       13:10:57        test_dList (EURUSD,H1)  
GK      0       13:10:57        test_dList (EURUSD,H1)  =======renewed List #2=======
PR      0       13:10:57        test_dList (EURUSD,H1)  Node #1, val=11 
QI      0       13:10:57        test_dList (EURUSD,H1)  Node #2, val=13 
QP      0       13:10:57        test_dList (EURUSD,H1)  Node #3, val=14 
LO      0       13:10:57        test_dList (EURUSD,H1)  Node #4, val=34 
JE      0       13:10:57        test_dList (EURUSD,H1)  Node #5, val=35 
HL      0       13:10:57        test_dList (EURUSD,H1)  Node #6, val=36 
FK      0       13:10:57        test_dList (EURUSD,H1)  Node #7, val=37 
DR      0       13:10:57        test_dList (EURUSD,H1)  Node #8, val=38 
FJ      0       13:10:57        test_dList (EURUSD,H1)  Node #9, val=666 
HO      0       13:10:57        test_dList (EURUSD,H1)  
JR      0       13:10:57        test_dList (EURUSD,H1)  

単独で接続されたリストの場合、そのスクリプトは、最初のリストにデータを格納し、2番目のリストにコピー&ペーストします。そして、2番目のノード数を増やし、リストをソート後、表示します。


2.6展開され二重で接続されたリスト

この種類のリストは、値だけではなく、配列全体も保存することができます。

CiUnrollDoubleList 型のリストの基盤を作りましょう(図12)

CiUnrollDoubleListクラスモデル

図12 CiUnrollDoubleListクラスモデル

ここではデータ配列を扱うので、関節的なベースクラスCiSingleListにて定義されたメソッドを再定義する必要があります。

CiUnrollDoubleListクラスの定義が以下にあります。

//+------------------------------------------------------------------+
//|                     CiUnrollDoubleList class                     |
//+------------------------------------------------------------------+
class CiUnrollDoubleList : public CDoubleList
  {
public:
   void              CiUnrollDoubleList(void);                      // default constructor
   void              CiUnrollDoubleList(int &_node_arr[]);          // parameterized constructor
   void             ~CiUnrollDoubleList(void){TRACE_CALL(_t_flag)}; // destructor
   //---
   virtual void      AddFront(int &_node_arr[]);                    // add a new node to the beginning of the list
   virtual void      AddRear(int &_node_arr[]);                     // add a new node to the end of the list
   virtual bool      CopyByValue(const CiSingleList &_udList);      // copy by values
   virtual void      PrintList(string _caption=NULL);               // print the list
   virtual void      BubbleSort(void);                              // bubble sorting

protected:
   virtual bool      AddToEmpty(int &_node_arr[]);                  // add a node to an empty list
   virtual void      addFront(int &_node_arr[]);                    // add a new "native" node to the beginning of the list
   virtual void      addRear(int &_node_arr[]);                     // add a new "native" node to the end of the list
   virtual int       removeFront(void);                             // delete the "native" node from the beginning of the list
   virtual int       removeRear(void);                              // delete the "native" node from the end of the list
   virtual void      deleteNodeByIndex(const uint _idx);            // delete the "native" ith node from the list
   virtual CiSingleNode *newNode(int &_node_arr[]);                 // new "native" node
  };

そのクラスメソッドの完全な定義は、CiUnrollDoubleList.mqhにて提供されています。

クラスメソッドの処理をチェックするためにtest_UdList.mq5スクリプトを動かしましょう。ここでは、ノードの処理は、以前のスクリプトにて使用されているものに似ています。おそらくソートと出力メソッドに関していくつか述べるいう必要があります。ソートメソッドは、要素の数によってノードを整理し、最小サイズの値の配列を含むノードはリストの上に来るようになります。

出力メソッドは、特定のノードに所持されている配列の値を表示します。

そのスクリプトを稼働後、ログは以下のエントリを表示します;

II      0       13:22:23        test_UdList (EURUSD,H1) =======List #1=======
FN      0       13:22:23        test_UdList (EURUSD,H1) List node #1, array: 55, 12, 1, 2, 11, 114, 33, 113, 14, 15, 16, 17, 18, 19, 20
OO      0       13:22:23        test_UdList (EURUSD,H1) List node #2, array: 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
GG      0       13:22:23        test_UdList (EURUSD,H1) 
GP      0       13:22:23        test_UdList (EURUSD,H1) 
GR      0       13:22:23        test_UdList (EURUSD,H1) =======List #2 before sorting=======
JO      0       13:22:23        test_UdList (EURUSD,H1) List node #1, array: 55, 12, 1, 2, 11, 114, 33, 113, 14, 15, 16, 17, 18, 19, 20
CH      0       13:22:23        test_UdList (EURUSD,H1) List node #2, array: 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
CF      0       13:22:23        test_UdList (EURUSD,H1) List node #3, array: -89, -131, -141, -139, -129, -25, -105, -24, -122, -120, -118, -116, -114, -112, -110
GD      0       13:22:23        test_UdList (EURUSD,H1) 
GQ      0       13:22:23        test_UdList (EURUSD,H1) 
LJ      0       13:22:23        test_UdList (EURUSD,H1) =======List #2 after sorting=======
FN      0       13:22:23        test_UdList (EURUSD,H1) List node #1, array: 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
CJ      0       13:22:23        test_UdList (EURUSD,H1) List node #2, array: 55, 12, 1, 2, 11, 114, 33, 113, 14, 15, 16, 17, 18, 19, 20
II      0       13:22:23        test_UdList (EURUSD,H1) List node #3, array: -89, -131, -141, -139, -129, -25, -105, -24, -122, -120, -118, -116, -114, -112, -110
MD      0       13:22:23        test_UdList (EURUSD,H1) 
MQ      0       13:22:23        test_UdList (EURUSD,H1)

ご覧の通り、ソート後、udList2リストは最小の配列から最大の配列を持つノードの順で表示されています。


2.7円の二重で接続されたリスト

非線形リストはこの記事では紹介されませんが、それらも扱うべきです。上記にてどのように縁のようにノードを接続するかについて示されています。

CiCircleDoubleList クラスのモデル(図13)を作成しましょうこのクラスは、CDoubleListクラスの派生クラスになります。

CiCircleDoubleList クラスモデル

図13 CiCircleDoubleList クラスモデル

このリストのノードは、特定の文字からなるという事実のため(ヘッドからテールが接続されています)、ソースのベースクラスCiSingleListのほぼすべてのメソッドが仮想化される必要があります。

//+------------------------------------------------------------------+
//|                      CiCircleDoubleList class                    |
//+------------------------------------------------------------------+
class CiCircleDoubleList : public CDoubleList
  {
public:
   void              CiCircleDoubleList(void);                       // default constructor
   void              CiCircleDoubleList(int _node_val);              // parameterized constructor
   void             ~CiCircleDoubleList(void){TRACE_CALL(_t_flag)};  // destructor
   //---
   virtual uint      Size(void) const;                                        // list size
   virtual bool      SetNodeByIndex(CiSingleNode *_new_node,const uint _idx); // insert the new ith node in the list
   virtual int       GetValByIndex(const uint _idx) const;           // value of the ith node in the list
   virtual CiSingleNode *GetNodeByIndex(const uint _idx) const;      // get the ith node in the list
   virtual bool      Find(const int _node_val) const;                // find the required value
   virtual bool      CopyByValue(const CiSingleList &_sList);        // copy the list by values

protected:
   virtual void      addFront(int _node_val);                        // add a new "native" node to the beginning of the list
   virtual void      addRear(int _node_val);                         // add a new "native" node to the end of the list
   virtual int       removeFront(void);                              // delete the "native" head node
   virtual int       removeRear(void);                               // delete the "native" tail node
   virtual void      deleteNodeByIndex(const uint _idx);             // delete the "native" ith node from the list

protected:
   void              CalcSize(void) const;                           // calculate the list size
   void              LinkHeadTail(void);                             // link head to tail
  };

CiCircleDoubleList.mqhにてそのクラスのすべての詳細が記載されています。

そのクラスのいくつかのメソッドを見てみましょう。CiCircleDoubleList::LinkHeadTail() メソッドは、テールノードとヘッドノードをつなげます。新しいてるかヘッドがある時や以前のリンクが失われた際に呼ばれる必要があります。

//+------------------------------------------------------------------+
//|                  Linking head to tail                            |
//+------------------------------------------------------------------+
void CiCircleDoubleList::LinkHeadTail(void)
  {
   TRACE_CALL(_t_flag)
   this.m_head.SetPrevNode(this.m_tail);      // link head to tail
   this.m_tail.SetNextNode(this.m_head);      // link tail to head
  }

円の単独で接続されたリストを扱う際、このメソッドがどのようになるか考えてみてください。

例えば、CiCircleDoubleList::addFront()メソッドを考えてみましょう。

//+------------------------------------------------------------------+
//|                New "native" node to the beginning of the list    |
//+------------------------------------------------------------------+
void CiCircleDoubleList::addFront(int _node_val)
  {
   TRACE_CALL(_t_flag)
   CDoubleList::addFront(_node_val); // call a similar method of the base class
   this.LinkHeadTail();              // link head and tail
  }

ベースクラスCDoubleListの似たメソッドが呼ばれていることがわかります。この時点にて、メソッドの処理を終了する必要があります。(そのメソッドは基本的にここでは必要ではありません。)ヘッドとテール間のリンクは、それなしでは円で接続されません。そのため、ヘッドとテールを接続するメソッドを呼ぶ必要があります。

test_UdList.mq5スクリプトにて円で接続されるリストを扱ったか否かがチェックされます。

タスクと目的の観点にて、使用されているその他のメソッドは以前の例と同じものです。

結果として、ログは以下のエントリを表示します:

PR      0       13:34:29        test_CdList (EURUSD,H1) =======List #1=======
QS      0       13:34:29        test_CdList (EURUSD,H1) Node #1, val=14 
QI      0       13:34:29        test_CdList (EURUSD,H1) Node #2, val=666 
LQ      0       13:34:29        test_CdList (EURUSD,H1) Node #3, val=13 
OH      0       13:34:29        test_CdList (EURUSD,H1) Node #4, val=11 
DP      0       13:34:29        test_CdList (EURUSD,H1) 
DK      0       13:34:29        test_CdList (EURUSD,H1) 
DI      0       13:34:29        test_CdList (EURUSD,H1) =======List #2 before sorting=======
MS      0       13:34:29        test_CdList (EURUSD,H1) Node #1, val=38 
IJ      0       13:34:29        test_CdList (EURUSD,H1) Node #2, val=37 
IQ      0       13:34:29        test_CdList (EURUSD,H1) Node #3, val=36 
EH      0       13:34:29        test_CdList (EURUSD,H1) Node #4, val=35 
EO      0       13:34:29        test_CdList (EURUSD,H1) Node #5, val=34 
FF      0       13:34:29        test_CdList (EURUSD,H1) Node #6, val=14 
DN      0       13:34:29        test_CdList (EURUSD,H1) Node #7, val=666 
GD      0       13:34:29        test_CdList (EURUSD,H1) Node #8, val=13 
JK      0       13:34:29        test_CdList (EURUSD,H1) Node #9, val=11 
JM      0       13:34:29        test_CdList (EURUSD,H1) 
JH      0       13:34:29        test_CdList (EURUSD,H1) 
MS      0       13:34:29        test_CdList (EURUSD,H1) =======List #2 after sorting=======
LE      0       13:34:29        test_CdList (EURUSD,H1) Node #1, val=11 
KL      0       13:34:29        test_CdList (EURUSD,H1) Node #2, val=13 
QS      0       13:34:29        test_CdList (EURUSD,H1) Node #3, val=14 
NJ      0       13:34:29        test_CdList (EURUSD,H1) Node #4, val=34 
NQ      0       13:34:29        test_CdList (EURUSD,H1) Node #5, val=35 
NH      0       13:34:29        test_CdList (EURUSD,H1) Node #6, val=36 
NO      0       13:34:29        test_CdList (EURUSD,H1) Node #7, val=37 
NF      0       13:34:29        test_CdList (EURUSD,H1) Node #8, val=38 
JN      0       13:34:29        test_CdList (EURUSD,H1) Node #9, val=666 
RJ      0       13:34:29        test_CdList (EURUSD,H1) 
RE      0       13:34:29        test_CdList (EURUSD,H1)

したがって、導入されたリストクラス間の継承における最終的なダイヤグラムは以下の通りです(図14)

すべてのクラスが継承によって関連付けられる必要があるかはわかりませんが、このように記載しておきます。

リストクラス間の継承

図14 リストクラス間の継承

カスタムリストの記述を扱ったこの記事のセクションを終わり、非線形リスト、倍増リストやその他のものに触れていません。関連する情報を集め、そのような動的なデータ構造を扱う経験をさらに養い、また別の記事にて紹介したいと思います。


3. MQL5標準ライブラリのリスト

標準ライブラリにて使用できるリストクラスをご覧ください(図15)

Data Classesに所属しています。

CListクラスモデル

図15 CList クラスモデル

CList は、実は CObjectクラスの派生クラスです。つまり、そのリストは、ノードであるクラスのメソッドやデータを継承しています。

そのリストクラスは、複数の印象的なメソッドを所持しています。正直に言うと、標準ライブラリにそのような大きいクラスを見つけるとは思っていませんでした。

CList クラスは8つのデータメンバーを持ちます。いくつかの点を指摘したいと思います。そのクラス属性は、現在のノードのインデックス (int m_curr_idx)を持ち、そのノードのポインター(CObject* m_curr_node)も所持しています。そんリストは「スマート」だということもでき、管理がローカル化している場所を示しています。さらに、メモリ管理メカニズムを特徴としています(物理的にノードを削除したり、リストから省くことができます。)

メソッドといえば 、CListクラスのすべてのメソッドは以下のグループに分けられます;

通常通り、標準コンストラクターとデストラクターがあります。

一つは、すべてのポインターを空(NULL)にします。そのメモリ管理フラッグの状態は削除にセットされます。新しいリストはソートされません。

そのデストラクターは、Clear()メソッドを呼び出し、ノードのリストを空にします。そのリストが消える際、必ずしもその要素(ノード)の「死」を意味しません。したがって、そのリストの要素を削除する際にセットされるメモリ管理フラッグは、クラスの関係を合成から集合に変化させます。

set-、get-メソッドFreeMode()を使用してこのフラッグを扱います。

リストの拡張を可能にするクラスの二つのメソッドがあります;Add()とInsert()です。一番目は、この記事の最初の章にて使用されたAddRear()メソッドに類似しています。2番めのメソッドは、SetNodeByIndex()メソッドに似ています。

簡単な例から始めましょう。インターフェースクラスCObjectの派生クラス、CNodeIntノードクラスを最初に作成する必要があります。Int型の値を保存します。

//+------------------------------------------------------------------+
//|                        CNodeInt class                            |
//+------------------------------------------------------------------+
class  CNodeInt : public CObject
  {
private:
   int               m_val;  // node data

public:
   void              CNodeInt(void){this.m_val=WRONG_VALUE;}; // default constructor
   void              CNodeInt(int _val);                      // parameterized constructor
   void             ~CNodeInt(void){};                        // destructor
   int               GetVal(void){return this.m_val;};        // get-method for node data
   void              SetVal(int _val){this.m_val=_val;};      // set-method for node data
  };
//+------------------------------------------------------------------+
//|                    Parameterized constructor                     |
//+------------------------------------------------------------------+
void CNodeInt::CNodeInt(int _val):m_val(_val)
  {

  };

test_MQL5_List.mq5スクリプトのCList リストを扱います。

例1は、リストとノードの動的な作成を示しています。そのリストは、ノードが格納され、最初のノードの値はリストの削除前と後にチェックされます。

//--- Example 1 (testing memory management)
   CList *myList=new CList;
// myList.FreeMode(false);  // reset flag
   bool _free_mode=myList.FreeMode();
   PrintFormat("\nList \"myList\" - memory management flag: %d",_free_mode);
   CNodeInt *p_new_nodes_int[10];
   p_new_nodes_int[0]=NULL;
   for(int i=0;i<ArraySize(p_new_nodes_int);i++)
     {
      p_new_nodes_int[i]=new CNodeInt(rand());
      myList.Add(p_new_nodes_int[i]);
     }
   PrintFormat("List \"myList\" has as many nodes as: %d",myList.Total());
   Print("=======Before deleting \"myList\"=======");
   PrintFormat("The 1st node value is: %d",p_new_nodes_int[0].GetVal());
   delete myList;
   int val_to_check=WRONG_VALUE;
   if(CheckPointer(p_new_nodes_int[0]))
      val_to_check=p_new_nodes_int[0].GetVal();
   Print("=======After deleting \"myList\"=======");
   PrintFormat("The 1st node value is: %d",val_to_check);

もしそのフラッグをリセットするStringがコメントアウトされていれば、ログにて以下のエントリを取得します。

GS      0       14:00:16        test_MQL5_List (EURUSD,H1)      
EO      0       14:00:16        test_MQL5_List (EURUSD,H1)      List "myList" - memory management flag: 1
FR      0       14:00:16        test_MQL5_List (EURUSD,H1)      List "myList" has as many nodes as: 10
JH      0       14:00:16        test_MQL5_List (EURUSD,H1)      =======Before deleting "myList"=======
DO      0       14:00:16        test_MQL5_List (EURUSD,H1)      The 1st node value is: 7189
KJ      0       14:00:16        test_MQL5_List (EURUSD,H1)      =======After deleting "myList"=======
QK      0       14:00:16        test_MQL5_List (EURUSD,H1)      The 1st node value is: -1

動的にmyListリストを削除した後、その中の全てのノードはメモリから削除されることに注意してください。

しかし、リセットフラッグStringをコメントアウトを外した場合;

// myList.FreeMode(false); // reset flag

ログの出力は以下のようになります:

NS      0       14:02:11        test_MQL5_List (EURUSD,H1)      
CN      0       14:02:11        test_MQL5_List (EURUSD,H1)      List "myList" - memory management flag: 0
CS      0       14:02:11        test_MQL5_List (EURUSD,H1)      List "myList" has as many nodes as: 10
KH      0       14:02:11        test_MQL5_List (EURUSD,H1)      =======Before deleting "myList"=======
NL      0       14:02:11        test_MQL5_List (EURUSD,H1)      The 1st node value is: 20411
HJ      0       14:02:11        test_MQL5_List (EURUSD,H1)      =======After deleting "myList"=======
LI      0       14:02:11        test_MQL5_List (EURUSD,H1)      The 1st node value is: 20411
QQ      1       14:02:11        test_MQL5_List (EURUSD,H1)      10 undeleted objects left
DD      1       14:02:11        test_MQL5_List (EURUSD,H1)      10 objects of type CNodeInt left
DL      1       14:02:11        test_MQL5_List (EURUSD,H1)      400 bytes of leaked memory

そのリストが削除されるの両方でヘッドノードがその値を保持していることに気づくと思います。この場合、そのスクリプトがそれらを削除するためのコードを所持していないのであれば、削除されていないオブジェクトもまた残っています。

それでは、ソートメソッドを扱ってみましょう。

//--- Example 2 (sorting)

   CList *myList=new CList;
   CNodeInt *p_new_nodes_int[10];
   p_new_nodes_int[0]=NULL;
   for(int i=0;i<ArraySize(p_new_nodes_int);i++)
     {
      p_new_nodes_int[i]=new CNodeInt(rand());
      myList.Add(p_new_nodes_int[i]);
     }
   PrintFormat("\nList \"myList\" has as many nodes as: %d",myList.Total());
   Print("=======List \"myList\" before sorting=======");
   for(int i=0;i<myList.Total();i++)
     {
      CNodeInt *p_node_int=myList.GetNodeAtIndex(i);
      int node_val=p_node_int.GetVal();
      PrintFormat("Node #%d is equal to: %d",i+1,node_val);
     }
   myList.Sort(0);
   Print("\n=======List \"myList\" after sorting=======");
   for(int i=0;i<myList.Total();i++)
     {
      CNodeInt *p_node_int=myList.GetNodeAtIndex(i);
      int node_val=p_node_int.GetVal();
      PrintFormat("Node #%d is equal to: %d",i+1,node_val);
     }
   delete myList;

結果として、ログは以下のエントリを表示します:

OR      0       22:47:01        test_MQL5_List (EURUSD,H1)      
FN      0       22:47:01        test_MQL5_List (EURUSD,H1)      List "myList" has as many nodes as: 10
FH      0       22:47:01        test_MQL5_List (EURUSD,H1)      =======List "myList" before sorting=======
LG      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #1 is equal to: 30511
CO      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #2 is equal to: 17404
GF      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #3 is equal to: 12215
KQ      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #4 is equal to: 31574
NJ      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #5 is equal to: 7285
HP      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #6 is equal to: 23509
IH      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #7 is equal to: 26991
NS      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #8 is equal to: 414
MK      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #9 is equal to: 18824
DR      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #10 is equal to: 1560
OR      0       22:47:01        test_MQL5_List (EURUSD,H1)      
OM      0       22:47:01        test_MQL5_List (EURUSD,H1)      =======List "myList" after sorting=======
QM      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #1 is equal to: 26991
RE      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #2 is equal to: 23509
ML      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #3 is equal to: 18824
DD      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #4 is equal to: 414
LL      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #5 is equal to: 1560
IG      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #6 is equal to: 17404
PN      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #7 is equal to: 30511
II      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #8 is equal to: 31574
OQ      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #9 is equal to: 12215
JH      0       22:47:01        test_MQL5_List (EURUSD,H1)      Node #10 is equal to: 7285

たとえソートがなされていたとしても、そのソート技術は私にとってミステリーのようなものです。その理由を説明致します。呼び出し注文に関して詳しくいかず、CList::Sort()メソッドは、ベースクラスでは実装されていない仮想メソッドCObject::Compare() を呼び出します。そのため、プログラマーは自身でソートメソッドの実装を扱う必要があります。

そして、 Total()メソッドに関してですが、データメンバーm_data_totalが担当する要素の数を返します。それはかなり短く簡潔なメソッドです。この実装におけるその要素のカウントは、以前紹介したものよりもかなり早くなっています。実際、そのリストのノードの正確な数がノードの追加時・削除時にセットされます。

例3h、CListCiSingleList型のリストの格納スピードを比較し、それぞれのリストのサイズの取得のための時間を計算します。

//--- Example 3 (nodes number)

   int iterations=1e7;   // 10 million iterations
//--- the new CList 
   CList *p_mql_List=new CList;
   uint start=GetTickCount(); // starting value
   for(int i=0;i<iterations;i++)
     {
      CNodeInt *p_node_int=new CNodeInt(rand());
      p_mql_List.Add(p_node_int);
     }
   uint time=GetTickCount()-start; // time spent, msec
   Print("\n=======the CList type list=======");
   PrintFormat("Filling the list of %.3e nodes has taken %d msec",iterations,time);
//--- get the size
   start=GetTickCount();
   int list_size=p_mql_List.Total(); 
   time=GetTickCount()-start;
   PrintFormat("Getting the size of the list has taken %d msec",time);

   delete p_mql_List;

//---  the new CiSingleList 
   CiSingleList *p_sList=new CiSingleList;

   start=GetTickCount(); // starting value
   for(int i=0;i<iterations;i++)
      p_sList.AddRear(rand());
   time=GetTickCount()-start; // time spent, msec
   Print("\n=======the CiSingleList type list=======");
   PrintFormat("Filling the list of %.3e nodes has taken %d msec",iterations,time);
//--- get the size
   start=GetTickCount();
   list_size=(int)p_sList.Size();  
   time=GetTickCount()-start;
   PrintFormat("Getting the size of the list has taken %d msec",time);
   delete p_sList;  

以下がログに表示されます。

KO      0       22:48:24        test_MQL5_List (EURUSD,H1)      
CK      0       22:48:24        test_MQL5_List (EURUSD,H1)      =======the CList type list=======
JL      0       22:48:24        test_MQL5_List (EURUSD,H1)      Filling the list of 1.000e+007 nodes has taken 2606 msec
RO      0       22:48:24        test_MQL5_List (EURUSD,H1)      Getting the size of the list has taken 0 msec
LF      0       22:48:29        test_MQL5_List (EURUSD,H1)      
EL      0       22:48:29        test_MQL5_List (EURUSD,H1)      =======the CiSingleList type list=======
KK      0       22:48:29        test_MQL5_List (EURUSD,H1)      Filling the list of 1.000e+007 nodes has taken 2356 msec
NF      0       22:48:29        test_MQL5_List (EURUSD,H1)      Getting the size of the list has taken 359 msec

そのサイズを取得するためのメソッドは、CListリストにて作動します。ところで、そのリストへのノードの追加もかなり早いです。

次のブロック(例4)では、データコンテナとしてそのリストの下降の一つに注意してください。そのリストの要素は直線的にアクセスされます。CListクラスでは、二進法にてアクセスされ、わずかにそのアルゴリズムの面倒さを削減します。

直線的に検索する際に、面倒さはO(N)です。二進法にて実行される検索は、log2(N)の煩雑さにつながります。

これは、データセットの要素にアクセスするためのコードの例です。

//--- Example 4 (speed of accessing the node)

   const uint Iter_arr[]={1e3,3e3,6e3,9e3,1e4,3e4,6e4,9e4,1e5,3e5,6e5};
   for(uint i=0;i<ArraySize(Iter_arr);i++)
     {
      const uint cur_iterations=Iter_arr[i]; // iterations number     
      uint randArr[];                        // array of random numbers
      uint idxArr[];                         // array of indexes
      //--- set the arrays size    
      ArrayResize(randArr,cur_iterations);
      ArrayResize(idxArr,cur_iterations);
      CRandom myRand;                        // random number generator
      //--- fill the array of random numbers
      for(uint t=0;t<cur_iterations;t++)
         randArr[t]=myRand.int32();
      //--- fill the array of indexes with random numbers (from 0 to 10 million)
      int iter_log10=(int)log10(cur_iterations);
      for(uint r=0;r<cur_iterations;r++)
        {
         uint rand_val=myRand.int32(); // random value (from 0 to 4 294 967 295)
         if(rand_val>=cur_iterations)
           {
            int val_log10=(int)log10(rand_val);
            double log10_remainder=val_log10-iter_log10;
            rand_val/=(uint)pow(10,log10_remainder+1);
           }
         //--- check the limit
         if(rand_val>=cur_iterations)
           {
            Alert("Random value error!");
            return;
           }
         idxArr[r]=rand_val;
        }
      //--- time spent for the array
      uint start=GetTickCount();
      //--- accessing the array elements 
      for(uint p=0;p<cur_iterations;p++)
         uint random_val=randArr[idxArr[p]];

      uint time=GetTickCount()-start; // time spent, msec
      Print("\n=======the uint type array=======");
      PrintFormat("Random accessing the array of elements %.1e has taken %d msec",cur_iterations,time);

      //--- the CList type list
      CList *p_mql_List=new CList;
      //--- fill the list
      for(uint q=0;q<cur_iterations;q++)
        {
         CNodeInt *p_node_int=new CNodeInt(randArr[q]);
         p_mql_List.Add(p_node_int);
        }
      start=GetTickCount();
      //--- accessing the list nodes
      for(uint w=0;w<cur_iterations;w++)
         CNodeInt *p_node_int=p_mql_List.GetNodeAtIndex(idxArr[w]);
      time=GetTickCount()-start; // time spent, msec
      Print("\n=======the CList type list=======");
      PrintFormat("Random accessing the list of nodes %.1e has taken %d msec",cur_iterations,time);

      //--- free the memory
      ArrayFree(randArr);
      ArrayFree(idxArr);
      delete p_mql_List;
     }

ブロック処理結果ん基づいて、以下のエントリがログに出力されます:

MR      0       22:51:22        test_MQL5_List (EURUSD,H1)      
QL      0       22:51:22        test_MQL5_List (EURUSD,H1)      =======the uint type array=======
IG      0       22:51:22        test_MQL5_List (EURUSD,H1)      Random accessing the array of elements 1.0e+003 has taken 0 msec
QF      0       22:51:22        test_MQL5_List (EURUSD,H1)      
IQ      0       22:51:22        test_MQL5_List (EURUSD,H1)      =======the CList type list=======
JK      0       22:51:22        test_MQL5_List (EURUSD,H1)      Random accessing the list of nodes 1.0e+003 has taken 0 msec
MJ      0       22:51:22        test_MQL5_List (EURUSD,H1)      
QD      0       22:51:22        test_MQL5_List (EURUSD,H1)      =======the uint type array=======
GO      0       22:51:22        test_MQL5_List (EURUSD,H1)      Random accessing the array of elements 3.0e+003 has taken 0 msec
QN      0       22:51:22        test_MQL5_List (EURUSD,H1)      
II      0       22:51:22        test_MQL5_List (EURUSD,H1)      =======the CList type list=======
EP      0       22:51:22        test_MQL5_List (EURUSD,H1)      Random accessing the list of nodes 3.0e+003 has taken 16 msec
OR      0       22:51:22        test_MQL5_List (EURUSD,H1)      
OL      0       22:51:22        test_MQL5_List (EURUSD,H1)      =======the uint type array=======
FG      0       22:51:22        test_MQL5_List (EURUSD,H1)      Random accessing the array of elements 6.0e+003 has taken 0 msec
CF      0       22:51:22        test_MQL5_List (EURUSD,H1)      
GQ      0       22:51:22        test_MQL5_List (EURUSD,H1)      =======the CList type list=======
CH      0       22:51:22        test_MQL5_List (EURUSD,H1)      Random accessing the list of nodes 6.0e+003 has taken 31 msec
QJ      0       22:51:22        test_MQL5_List (EURUSD,H1)      
MD      0       22:51:22        test_MQL5_List (EURUSD,H1)      =======the uint type array=======
MO      0       22:51:22        test_MQL5_List (EURUSD,H1)      Random accessing the array of elements 9.0e+003 has taken 0 msec
EN      0       22:51:22        test_MQL5_List (EURUSD,H1)      
MJ      0       22:51:22        test_MQL5_List (EURUSD,H1)      =======the CList type list=======
CP      0       22:51:22        test_MQL5_List (EURUSD,H1)      Random accessing the list of nodes 9.0e+003 has taken 47 msec
CR      0       22:51:22        test_MQL5_List (EURUSD,H1)      
KL      0       22:51:22        test_MQL5_List (EURUSD,H1)      =======the uint type array=======
JG      0       22:51:22        test_MQL5_List (EURUSD,H1)      Random accessing the array of elements 1.0e+004 has taken 0 msec
GF      0       22:51:22        test_MQL5_List (EURUSD,H1)      
KR      0       22:51:22        test_MQL5_List (EURUSD,H1)      =======the CList type list=======
MK      0       22:51:22        test_MQL5_List (EURUSD,H1)      Random accessing the list of nodes 1.0e+004 has taken 343 msec
GJ      0       22:51:22        test_MQL5_List (EURUSD,H1)      
GG      0       22:51:22        test_MQL5_List (EURUSD,H1)      =======the uint type array=======
LO      0       22:51:22        test_MQL5_List (EURUSD,H1)      Random accessing the array of elements 3.0e+004 has taken 0 msec
QO      0       22:51:24        test_MQL5_List (EURUSD,H1)      
MJ      0       22:51:24        test_MQL5_List (EURUSD,H1)      =======the CList type list=======
NP      0       22:51:24        test_MQL5_List (EURUSD,H1)      Random accessing the list of nodes 3.0e+004 has taken 1217 msec
OS      0       22:51:24        test_MQL5_List (EURUSD,H1)      
KO      0       22:51:24        test_MQL5_List (EURUSD,H1)      =======the uint type array=======
CP      0       22:51:24        test_MQL5_List (EURUSD,H1)      Random accessing the array of elements 6.0e+004 has taken 0 msec
MG      0       22:51:26        test_MQL5_List (EURUSD,H1)      
ER      0       22:51:26        test_MQL5_List (EURUSD,H1)      =======the CList type list=======
PG      0       22:51:26        test_MQL5_List (EURUSD,H1)      Random accessing the list of nodes 6.0e+004 has taken 2387 msec
GK      0       22:51:26        test_MQL5_List (EURUSD,H1)      
OG      0       22:51:26        test_MQL5_List (EURUSD,H1)      =======the uint type array=======
NH      0       22:51:26        test_MQL5_List (EURUSD,H1)      Random accessing the array of elements 9.0e+004 has taken 0 msec
JO      0       22:51:30        test_MQL5_List (EURUSD,H1)      
NK      0       22:51:30        test_MQL5_List (EURUSD,H1)      =======the CList type list=======
KO      0       22:51:30        test_MQL5_List (EURUSD,H1)      Random accessing the list of nodes 9.0e+004 has taken 3619 msec
HS      0       22:51:30        test_MQL5_List (EURUSD,H1)      
DN      0       22:51:30        test_MQL5_List (EURUSD,H1)      =======the uint type array=======
RP      0       22:51:30        test_MQL5_List (EURUSD,H1)      Random accessing the array of elements 1.0e+005 has taken 0 msec
OD      0       22:52:05        test_MQL5_List (EURUSD,H1)      
GS      0       22:52:05        test_MQL5_List (EURUSD,H1)      =======the CList type list=======
DE      0       22:52:05        test_MQL5_List (EURUSD,H1)      Random accessing the list of nodes 1.0e+005 has taken 35631 msec
NH      0       22:52:06        test_MQL5_List (EURUSD,H1)      
RF      0       22:52:06        test_MQL5_List (EURUSD,H1)      =======the uint type array=======
FI      0       22:52:06        test_MQL5_List (EURUSD,H1)      Random accessing the array of elements 3.0e+005 has taken 0 msec
HL      0       22:54:20        test_MQL5_List (EURUSD,H1)      
PD      0       22:54:20        test_MQL5_List (EURUSD,H1)      =======the CList type list=======
FN      0       22:54:20        test_MQL5_List (EURUSD,H1)      Random accessing the list of nodes 3.0e+005 has taken 134379 msec
RQ      0       22:54:20        test_MQL5_List (EURUSD,H1)      
JI      0       22:54:20        test_MQL5_List (EURUSD,H1)      =======the uint type array=======
MR      0       22:54:20        test_MQL5_List (EURUSD,H1)      Random accessing the array of elements 6.0e+005 has taken 15 msec
NE      0       22:58:48        test_MQL5_List (EURUSD,H1)      
FL      0       22:58:48        test_MQL5_List (EURUSD,H1)      =======the CList type list=======
GE      0       22:58:48        test_MQL5_List (EURUSD,H1)      Random accessing the list of nodes 6.0e+005 has taken 267589 msec

リスト要素へのランダムアクセスがそのリストサイズが大きくなるにつれ、時間がかかるようになることがわかります(図16)

配列やリスト要素へのランダムアクセスのために費やされる時間

図16 配列やリスト要素へのランダムアクセスのために費やされる時間

データの保存やローディングのためのメソッドを紹介します。

ベースリストクラスCListは、そのようなメソッドを含みますが、それらは仮想メソッドです。したがって、例を用いて処理をテストをするために準備する必要があります。

派生クラスCIntListを用いて、CListクラスの機能を継承する必要があります。後者は、新しい要素CIntList::CreateElement()を作成するためのメソッドが一つのみあります。

//+------------------------------------------------------------------+
//|                      CIntList class                              |
//+------------------------------------------------------------------+
class CIntList : public CList
  {

public:
   virtual CObject *CreateElement(void);
  };
//+------------------------------------------------------------------+
//|                    New element of the list                       |
//+------------------------------------------------------------------+
CObject *CIntList::CreateElement(void)
  {
   CObject *new_node=new CNodeInt();
   return new_node;
  }

仮想メソッドCNodeInt::Save()CNodeInt::Load()を、派生ノード型CNodeIntに追加する必要があります。それぞれCList::Save() CList::Load()メンバ関数から呼ばれます。

その例は以下の通りです(例5)

//--- Example 5 (saving list data)
//--- the CIntList type list
   CList *p_int_List=new CIntList;
   int randArr[1000];                        // array of random numbers
   ArrayInitialize(randArr,0);
//--- fill the array of random numbers 
   for(int t=0;t<1000;t++)
      randArr[t]=(int)myRand.int32();
//--- fill the list
   for(uint q=0;q<1000;q++)
     {
      CNodeInt *p_node_int=new CNodeInt(randArr[q]);
      p_int_List.Add(p_node_int);
     }
//--- save the list to the file 
   int  file_ha=FileOpen("List_data.bin",FILE_WRITE|FILE_BIN);
   p_int_List.Save(file_ha);
   FileClose(file_ha);             
   p_int_List.FreeMode(true);    
   p_int_List.Clear();           
//--- load the list from the file  
   file_ha=FileOpen("List_data.bin",FILE_READ|FILE_BIN);
   p_int_List.Load(file_ha);
   int Loaded_List_size=p_int_List.Total();
   PrintFormat("Nodes loaded from the file: %d",Loaded_List_size);
//--- free the memory     
   delete p_int_List;

チャートにてそのスクリプトを稼働後、以下のエントリは、ログに追加されます。

ND      0       11:59:35        test_MQL5_List (EURUSD,H1)     1000ものノードがそのファイルからロードされます。

ノード型CNodeIntのデータメンバーのための入力/出力メソッドの実装を紹介しました。

次のセクションでは、MQL5を扱う際にどにょうにリストが問題を解決するために使用されているのかの例を提示します。


4. MQL5のリストを用いた例

以前のセクションにて標準ライブラリCListクラスのソッドを紹介している際に、いくつかの例をすでに提示しています。

そのリストが特定の問題を解決するために使用されるケースを紹介します。そして、コンテナデータ型として、そのリストの利点を指摘します。リストの柔軟性を利用してより効果的にコードを扱えるようにできます。


4.1グラフィカルオブジェクトのハンドリング

チャートのグラフィカルオブジェクトを作成する必要があることを想像してください。これらは、様々な理由によりチャートにて表示される様々なオブジェクトです。

そのリストがグラフィカルオブジェクトに関する状況を改善するのいかに役に立ったか覚えています。そして、この経験をシェアしたいと思います。

特定の条件により垂直の線を作成する作業がありました。その条件に沿って、その垂直線は、長さがその時に応じて変わる特定のタイムインターバルにおける制限として機能しました。つまり、そのインターバルは、常に完全に形成されていませんでした。

EMA21の行動を勉強しており、そのために、統計を収集しなければなりませんでした。

特に、移動平均の傾斜の長さに興味がありました。例えば、降下の動きでは、その開始ポイントは移動平均の負の動き(つまり、値の降下)をレジスターすることにより特定されました。垂直線が描かれました。図17 ロウソク足のオープン直後、2013 16:00 9月5日 EURUSD、H1において特定された開始地点を示しています。

図17 降下インターバルの最初のポイント

図17 降下インターバルの最初のポイント

二番目のポイントは、降下の動きの終了が反対の原則、つまり、値の上昇、移動平均の正の動きをレジスターすることで、特定されました。

図18 降下のインターバルにおける二番目のポイント

図18 降下のインターバルにおける二番目のポイント

したがって、そのターゲットインターバルは、2013 9/5 16:00 から 2013 9/6 17:00までになります。

異なるインターバルの特定におけるいシステムは。より複雑か、よりシンプルなものになります。これは重要ではありません。重要なことは、グラフィカルオブジェクトを扱うための技術や、統計データの収集のための技術は、そのリストの主要な利点の一つ、合成の柔軟性に関連します。

例として、まず二つのグラフィカル「垂直線」オブジェクトに責任を持つCVertLineNode型のノードを作成しました。

そのクラス定義は以下の通りです;

//+------------------------------------------------------------------+
//|                      CVertLineNode class                         |
//+------------------------------------------------------------------+
class CVertLineNode : public CObject
  {
private:
   SVertLineProperties m_vert_lines[2];      // array of structures of vertical line properties
   uint              m_duration;             // frame duration
   bool              m_IsFrameFormed;        // flag of frame formation

public:
   void              CVertLineNode(void);
   void             ~CVertLineNode(void){};
   //--- set-methods   
   void              SetLine(const SVertLineProperties &_vert_line,bool IsFirst=true);
   void              SetDuration(const uint _duration){this.m_duration=_duration;};
   void              SetFrameFlag(const bool _frame_flag){this.m_IsFrameFormed=_frame_flag;};
   //--- get-methods   
   void              GetLine(SVertLineProperties &_vert_line_out,bool IsFirst=true) const;
   uint              GetDuration(void) const;
   bool              GetFrameFlag(void) const;
   //--- draw the line
   bool              DrawLine(bool IsFirst=true) const;
  };

基本的に、このノードクラスはフレームを記述します(二つの垂直線のうちにある多数のろうそく足として解釈されます)フレームの制限は、垂直線の属性や遅延期間、形成フラッグの構造によって示されます。

標準コンストラクターやデストラクターとは別に、そのクラスはいくつかのset-、get-メソッドを持ち、同様にチャートに線を描画するメソッドも持ちます。

例での垂直線は、降下の動きの開始を示す一番目の垂直線と上向きの動きの開始を示す二番目の垂直線がある時に、形成されることを思い出してください。

Stat_collector.mq5スクリプトを用いて、チャートの全てのフレームを表示し、いくつのノード(フレーム)がこの2000バーのうち、特定の期間において一致するのかを計測しました。

例として、いかなるフレーム含む4つのリストを作成しました。その最初のリストは、5までのロウソク足の数にてフレームを所持し、二番目は10までの数、三番目は15まで、四番目は無限に所持します。

NS      0       15:27:32        Stat_collector (EURUSD,H1)      =======List #1=======
RF      0       15:27:32        Stat_collector (EURUSD,H1)      Duration limit: 5
ML      0       15:27:32        Stat_collector (EURUSD,H1)      Nodes number: 65
HK      0       15:27:32        Stat_collector (EURUSD,H1)      
OO      0       15:27:32        Stat_collector (EURUSD,H1)      =======List #2=======
RI      0       15:27:32        Stat_collector (EURUSD,H1)      Duration limit: 10
NP      0       15:27:32        Stat_collector (EURUSD,H1)      Nodes number: 15
RG      0       15:27:32        Stat_collector (EURUSD,H1)      
FH      0       15:27:32        Stat_collector (EURUSD,H1)      =======List #3=======
GN      0       15:27:32        Stat_collector (EURUSD,H1)      Duration limit: 15
FG      0       15:27:32        Stat_collector (EURUSD,H1)      Nodes number: 6
FR      0       15:27:32        Stat_collector (EURUSD,H1)      
CD      0       15:27:32        Stat_collector (EURUSD,H1)      =======List #4=======
PS      0       15:27:32        Stat_collector (EURUSD,H1)      Nodes number: 20

結果として、以下のチャートを取得します(図19)利便性のために、2番目の垂直線は青色で表示されます。

図19 フレームの表示

最後のフレームは、2013年12/13の金曜日、最後の時間に形成されました。6時間の遅延であったため、二番目のリストに落ちています。

4.2仮想トレーディングを扱う

ティックフローにおいてのある証券に関して、いくつかの独立した戦略を実装するエキスパートアドバイザーを作成する必要があると想定してください。実際、一つの戦略のみが一つの証券に関して一度に実装できることは明確です。その他の戦略はすべて仮想的になります。なので、ただトレーディング案のテストと最適化を目的に実装されます。

ここでは、トレーディングや、特にMeta Trader 5に関する基礎的なコンセプトの詳細な内容を提供する根本的な記事を参照しなければなりません: "MetaTrader 5での注文、ポジション、取引".

なので、もしこの問題を解決する際に、そのトレーディングコンセプト、トレーディングオブジェクト管理システム、トレーディングオブジェクトに関する情報の保存方法、を用いたのであれば、仮想データベースの作成も考慮する必要があります。

開発者は、すべてのトレーディングオブジェクトを注文、ポジション、取引、過去の注文に分類します。その「トレーディングオブジェクト」という用語は著者自身により紹介されている言葉であると気づくかと思います。これは実際そうです。

仮想トレーディングにおいて類似したアプローチを使用し、以下の仮想トレーディングオブジェクト;仮想注文、仮想ポジション、仮想取引、仮想注文履歴を取得することをおすすめします。

この主題がより詳しい議論の的になることを信じています。一方、この記事の主題に戻り、リストを含む、コンテナ型データは仮想的な戦略を実装する際にプログラマーの生活を快適にするということを述べたいと思います。

トレードサーバーサイドにない新しい仮想ポジションを考えてみてください。つまり、それに関する情報はターミナル側にて保存されているということです。データベースは、いくつかのリストからなるリストによって表現され、その内の一つのリストは、仮想ポジションのノードを所持しています。

開発者のアプローチを用いた、仮想トレーディングの以下のクラスがあります。

クラス/グループ

詳細

CVirtualOrder

仮想 未決注文を扱うクラス

CVirtualHistoryOrder

仮想注文"履歴"を扱うクラス

CVirtualPosition

仮想オープンポジションを扱うクラス

CVirtualDeal

仮想 取引"履歴"を扱うクラス

CVirtualTrade

仮想 トレーディング処理を実行するためのクラス

図表 1. 仮想トレーディングクラス

仮想トレーディングクラスの合成は紹介しません。しかし、おそらく標準トレーディングクラスのほぼすべてのメソッドを所持しています。開発者が使用するものは、特定のトレーディングオブジェクトではなく、その属性のクラスであることに注意してください。

あなたのアルゴリズム内にてリストを使用するためには、ノードも必要になります。したがって、ノードにて仮想トレーディングオブジェクトクラスを包む必要があります。

その仮想オープンポジションのノードは、CVirtualPositionNode 型であると想定してください。このタイプの定義は以下のようになります:

//+------------------------------------------------------------------+
//|                Class CVirtualPositionNode                        |
//+------------------------------------------------------------------+
class CVirtualPositionNode : public CObject
  {
protected:
   CVirtualPositionNode *m_virt_position;         // pointer to the virtual function

public:
   void              CVirtualPositionNode(void);  // default constructor
   void             ~CVirtualPositionNode(void);  // destructor
  };

それでは、仮想ポジションがオープンした際、仮想ポジションのリストに追加されます。

データベースがランダムアクセスメモリに保存されているので、、仮想トレーディングオブジェクトを扱うことへのアプローチは、キャッシュメモリの使用は必要ではありません。もちろん、それがその他のストレージメディアに保存されるように調整できます。


結論

この記事では、リストなどのコンテナ型のデータ種類の利点を紹介しました。欠点を述べることなしにはすみませんでした。ただ、この情報がOOP、特に多相性などを勉強される方に役に立つことを願っています。

ファイルの場所:

個人的に、プロジェクトフォルダにファイルを作成し保存することをお勧めします。例えば、以下のようにです:%MQL5\Projects\UserLists. これがすべてのソースコードファイルを保存した場所です。もし標準のディレクトリを使用するのであれば、いくつかのファイルのコードにて、インクルードファイルの割り当てメソッドを変更する必要があります。

# ファイル ファイル位置 詳細
1 CiSingleNode.mqh %MQL5\Projects\UserLists 単独で接続されたリストノードのクラス
2 CDoubleNode.mqh %MQL5\Projects\UserLists 二重で接続されたリストノードのクラス
3 CiUnrollDoubleNode.mqh %MQL5\Projects\UserLists 円で二重に接続されたリストノードのクラス
4 test_nodes.mq5 %MQL5\Projects\UserLists ノードを扱う例付きのスクリプト
5 CiSingleList.mqh %MQL5\Projects\UserLists 単独で接続されたリストのクラス
6 CDoubleList.mqh %MQL5\Projects\UserLists 二重で接続されたリストのクラス
7 CiUnrollDoubleList.mqh %MQL5\Projects\UserLists 展開され二重で接続されたリストのクラス
8 CiCircleDoublList.mqh %MQL5\Projects\UserLists 円の二重で接続されたリストのクラス
9 test_sList.mq5 %MQL5\Projects\UserLists 単独で接続されたリストを扱う例付きのスクリプト
10 test_dList.mq5 %MQL5\Projects\UserLists 二重で接続されたリストを扱う例付きのスクリプト
11 test_UdList.mq5 %MQL5\Projects\UserLists 展開され二重で接続されたリストを扱う例付きのスクリプト
12 test_CdList.mq5 %MQL5\Projects\UserLists 円で二重に接続されたリストを扱う例付きのスクリプト
13 test_MQL5_List.mq5 %MQL5\Projects\UserLists CListクラスを扱う例付きのスクリプト
14 CNodeInt.mqh %MQL5\Projects\UserLists Integer型のノードのクラス
15 CIntList.mqh %MQL5\Projects\UserLists CNodeIntノードのためのリストクラス
16 CRandom.mqh %MQL5\Projects\UserLists ランダム数生成クラス
17 CVertLineNode.mqh %MQL5\Projects\UserLists 垂直線のフレームを扱うノードクラス
18 Stat_collector.mq5 %MQL5\Projects\UserLists 統計的なコレクション例のスクリプト

リファレンス:

  1. A. Friedman, L. Klander, M. Michaelis, H. Schildt. C/C++ Annotated Archives. Mcgraw-Hill Osborne Media, 1999. 1008 ページ
  2. V.D. Daleka, A.S. Derevyanko, O.G. Kravets, L.E. Timanovskaya. データモデルとストラクチャー学習ガイドKharkov, KhGPU, 2000. 241 ページ (ロシア語).