MQL5プログラミングベージックス:リスト
はじめに
MQL言語の新バージョンは、自動トレーディングシステムの開発者に複雑なタスクを実装するための効果的なツールを提供しています。その言語のプログラミングの機能がかなり拡大されたという事実を否定することは誰もできません。MQL5 OOP機能だけでもかなりの価値があります。また、標準ライブラリも紹介せずにはいられません。エラーコード番号358によって判断すると、クラステンプレートはすぐにサポートされるでしょう。
この記事では、データ型やデータセットを記載するテーマの続きであり、拡大判でもある内容を取り上げます。ここでは、MQL5.communityウェブサイトにて公開された記事を参照したいと思います。配列を扱うロジックや原則におけるとても詳細な記述は、Dmitry Fedoseev (Integer) の "MQL5 Programming Basics: Arrays"という記事にて紹介されています。
なので、今回はリスト、より正確に言うと、接続された線形リストに焦点を当てたいと思います。リストの構造、意味やロジックから始めます。そのあと、標準ライブラリにて使用可能な関連するツールを紹介します。MQL5を扱う際に、いかにリストが使用されているかの例を記載しています。
1. リストとノードのコンセプト:理論
開発者にとってリストとは何で、何をすればよいのでしょうか?この用語の定義について、公的な情報源であるWikipediaを参照します。
計算機科学にて、リストは、値の有限的な収集を実装する抽象的なデータ型で、同じ値が一度以上発生する場合があります。リストのインスタンスは、タプル - 数学的な概念の有限なシーケンスのコンピューターでの表現です。リストの値のインスタンスは、通常アイテムやエントリ、リストの要素と呼ばれます;もし同じ値が複数回発生すれば、それぞれが個別のアイテムとしてみなされます。
その用語「リスト」は、抽象的なリスト、特に接続されたリストを実装するために使用される複数の具体的なデータ構造のために使用されます。
この定義がかなり学者的であると感じるかと思います。
この記事において、この定義の最後の行にさらに興味を持ってもらいます。それでは見てみましょう:
計算機科学では、接続されたリストは、ノードからなるベーシックで動的なデータストラクチャーで、それぞれのノードは データと、リストの次か前のノードへの1つか2つのリファレンス を持ちます。[1]その従来の配列上の接続されたリストの基本的な利点は、構造的な柔軟性です;接続されたリストのアイテムは、コンピューターメモリーのデータ要素とマッチする必要はなく、リストアイテムの内部リンクは常にリストトラバーサルのためにに維持されます。
ステップごとに一つずつ見ていきましょう。
計算機科学では、リストそれ自体はいくつかのデータ型です。すでに作成しています。それは、複数のデータ型を含むので、むしろ統合的なデータ型と言えます。リストは幾分配列に似ています。もし一つのデータ型の配列が新しいデータ型として分類されれば、それはリストになるでしょう。しかし、完全にそういうわけではありません。
リストの基本的な利点は、必要であればリストのどの部分からでもノードの挿入や削除が可能であるという点です。リストは動的配列に似ています。ただ全くArrayResize()関数を使う必要はありません。
メモリー要素の順序について言うと、リストノードは保存されず、配列要素が近接のメモリ領域に保存されるような同じ方法で保存される必要がありません。
だいたいそのような感じです。さらに詳しく見ていきましょう。
1.1単独で接続されたリストのノード
リストはアイテムの代わりのノードの保存を可能にします。ノードは、二つの部分からなるデータ型です。
最初の部分はデータフィールドで、二つ目の部分はその他のノードとのリンクのために使用されます(図1)最初のノードは、「ヘッド」と呼ばれ、最後のノードは「テール」と呼ばれます。テールリンクフィールドは、NULLリファレンスを所持します。基本的にリストのさらなるノードの欠如を示すために使用されます。その他の特別なソースは、ヘッドのあとのリストの残りを「テール」とします。
図 1 単独で接続されたリストのノード
単独で接続されたリストノードとは別に異なる種類のノードがあります。二重で接続されたリストのノードは、おそらく最も共通のものです。
1.2二重で接続されたリストのノード
二重で接続されたリストのニーズを満たすノードが必要です。以前のノードを指す別のノードを所持するので、以前の種類とは異なります。そして、そのリストのヘッドのノードは、NULLリファレンスを所持します。そのノードを所持するリストの構造を表示しているダイアグラムにて、以前のノードを指すリンクが赤色の配列として表示されています。
図2 二重で接続されたリストの ノード
したがって、二重で接続されたリストのノードの機能は、単独で接続されたリストのノードのそれと似ています。以前のノードへの一つ以上のリンクを扱う必要があります。
1.3円で二重に接続されたリストのノード
上記のノードが非線形リストにて使用されるケースがあります。そして、記事では主に線形リストを紹介しますが、円のリストの例も紹介します。
図3 円で二重に接続されたリストのノード
円の二重で接続されたリストのダイアグラム(図3)は、二つのリンクフィールドを持つノードが単純に円で接続されています。これにはオレンジと緑の矢印が用いられます。そのため、ヘッドノードは、テールに接続されます。そして、テールノードのリンクフィールドは、ヘッドを指し空白ではありません。
1.4 主なリスト処理
以前述べられた通り、すべてのリストの処理は3つのグループに分けられます:
- 追加(リストへの新しいノードの追加);
- 削除(リストからのノードの削除)
- チェック(ノードのデータ)
追加メソッドは以下を含みます;
- リストの始まりに新しいノードを追加する;
- リストの終わりに新しいノードを追加する;
- リストの特定の位置にノードを追加する;
- 空のリストにノードを追加する;
- パラメーター化したコンストラクター;
削除の処理に関する限り、仮想的に追加グループの一致する処理をコピーしmさう。
- ヘッドノードの削除;
- テールノードの削除;
- リストの特定の位置からのノードの削除;
- デストラクター
デストラクターは、リストの処理を終了するだけではなく、適切にすべての要素を削除する役割を担います。
様々なチェック処理の3番目のグループは、リストのノードやノード値にアクセスします。
- 特定の値を探す;
- リスト空かチェックする;
- リストの特定のノードの値を取得する;
- リストの特定のノードへのポインターを取得する;
- リストのサイズを取得する;
- リストの要素の値を表示する
そのグループに追加して、4番目のサービスグループを分けたいと思います。以前のグループ群に以下を提供します;
- 割り当ての演算子、
- コピーコンストラクター、
- 動的ポインターを扱う,
- 値によりリストをコピーする;
- ソート
以上です。開発者は当然必要であればいつでもリストクラスの機能を拡大できます。
2. リストとノードのコンセプト:プログラミング
このパートでは、直接ノードとリストのプログラミングに進みたいと思います。コードの例は必要であれば提供されます。
2.1単独で接続されたリストのノード
単独で接続されたリストのニーズを満たすノードクラス(図4)の土台を築きましょう。"How to Develop an Expert Advisor using UML Tools(UMLツールを用いてどのようにエキスパートアドバイザーを開発するか)" という記事にてクラスのダイアグラムの観念を学びましょう。. (CTradeExpertクラスのUMLモデルを図5にてご覧ください。)
図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]配列におけるノード間のリンク
二重で接続されたリストのノードに進みましょう。
2.2二重で接続されたリストのノード
まずは、二重で接続されたリストが二つのポインター;次へのノードポインター、一つ前へのノードポイター、を持つため、異なっていまうs。すなわち、次へのノードリンクに加えて、単独で接続されたリストノードへのノードのポインターを加える必要があります。
このため、クラス関係として継承を使用することを勧めます。二重で接続されたノードにおけるクラスモデルは、以下の通りです(図 6).
図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]配列でのノード間のリンク
それでは、展開され二重で接続されたリストを作成する際に必要なノードを紹介します。
2.3展開され二重に接続されたリストのノード
すべての配列に帰することができるデータメンバーを含むノードを考えてみましょう。それは配列全体を含んでいます。展開されたリストを作成するために使用されるノードこのノードは二重で接続されたリストの標準のノードと完全に同じなので、ここでは例は提供しません。唯一の違いは、「データ」の属性がすべての配列をカプセル化することです。
継承を再度使用したいと思います。CDoubleNodeクラスは、展開され二重で接続されるリストのノードにおけるベースクラスとして機能します。そして、そのリストのノードにおけるクラスモデルは以下の通りです(図8)
図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)から作成しましょう。
図9 CiSingleList クラスモデル
CiSingleList クラスは、CiSingleNode 型のノードを使用することがわかると思います。クラス間の関係の種類といえば、以下のことが言えます;
- CiSingleListクラスは、 CiSingleNodeクラスを所持しています(構成)
- CiSingleListクラスは、 CiSingleNodeクラスメソッドを使用します(依存)
上記の関係の例は、図10にて提供されています。
図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にて提供されています:
図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)
図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クラスの派生クラスになります。
図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に所属しています。
図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、CListとCiSingleList型のリストの格納スピードを比較し、それぞれのリストのサイズの取得のための時間を計算します。
//--- 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 降下インターバルの最初のポイント
二番目のポイントは、降下の動きの終了が反対の原則、つまり、値の上昇、移動平均の正の動きをレジスターすることで、特定されました。
図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 | 統計的なコレクション例のスクリプト |
リファレンス:
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/709
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索