MQL5 プログラミングの基礎:ターミナルのグローバル変数
はじめに
MQL4/5 環境では、興味深いインスツルメント-クライアントターミナルのグローバル変数 、があります。それによりターミナルの全プログラムについて共有データストレージ領域を作成することができます。その上、この領域のライフタイムはテーミナルのクローズを停止させることはありません。本稿ではオブジェクト指向プログラミングツールによってターミナルのグローバル変数とは何であるのか明確な考えを得ます。
本稿では以降、特に指定のない限り、クライアントターミナルのグローバル変数を『グローバル変数』と呼びます。
1. グローバル変数、関数
プログラマーとしての視点からは、グローバル変数はトレーディングターミナルの動作中プログラムすべてにとって使用可能な名前付メモリ領域を言います。初心者のプログラマーは同時に動作している複数ターミナルがある場合、それらはすべてクローバル関数のために独自の独立したメモリスペースを有します。それらは重なることはありません。
言語開発者はドキュメンテーションで、グローバル変数を処理するためには11個の関数が使用される、と特定します。
理論は MQL4 教本の『グローバル変数』 セクションにあります。
次のセクションで私は、セットのタスクを実装するためにオブジェクト指向プログラミングインスツルメントを使用します。
2. クラス CGlobalVar
オブジェクト指向プログラミングの考え方に導かれ、クラス CGlobalVarを作成します。これは直接グローバル変数のオブジェクトに関わるものです。
//+------------------------------------------------------------------+ //| Class CGlobalVar | //+------------------------------------------------------------------+ class CGlobalVar : public CObject { //--- === Data members === --- private: string m_name; double m_value; //--- datetime m_create_time; datetime m_last_time; //--- flag for temporary var bool m_is_temp; //--- === Methods === --- public: //--- constructor/destructor void CGlobalVar(void); void CGlobalVar(const string _var_name,const double _var_val, const datetime _create_time); void ~CGlobalVar(void){}; //--- create/delete bool Create(const string _var_name,const double _var_val=0.0, const bool _is_temp=false); bool Delete(void); //--- exist bool IsGlobalVar(const string _var_name,bool _to_print=false); //--- set methods bool Value(const double _var_val); bool ValueOnCondition(const double _var_new_val,const double _var_check_val); //--- get methods string Name(void) const; datetime CreateTime(void) const; datetime LastTime(void); template<typename T> T GetValue(T _type) const; bool IsTemporary(void) const; //--- private: string FormName(const string _base_name,const bool _is_temp=false); };
クラスは何をインクルードしなければならないのでしょうか?最小限の属性リストとして、私は以下のプロパティを選択します。
- 変数名
- 変数値
- 作成時刻
- 最終呼び出し時刻
- 一時変数の特徴
メソッドについては以下です。
- 作成
- 削除
- 存在確認
- 新しい値の設定
- 条件付新しい値の設定
- 名前の受け取り
- 値の受け取り
- 一時変数フラグの受け取り
CGlobalVar::GetValue メソッドについては別にお話します。それはテンプレートメソッドです。それはユーザーが引数として設定する変数値に対するデータタイを返します。
ここでの問題は MQL では関数はパラメータによってのみ分類されることです。そのためフェイクのパラメータを追加する必要があります。
テストスクリプトGlobals_test1.mq5 を作成します。ここで CGlobalVar タイプのオブジェクトを処理します。
#include "CGlobalVar.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVar gVar1; //--- create a temporary global var if(gVar1.Create("Gvar1",3.123456789101235,true)) { Print("\n---=== A new global var ===---"); PrintFormat("Name: \"%s\"",gVar1.Name()); PrintFormat("Is temporary: %d",gVar1.IsTemporary()); //--- Get the value //--- double type double d=0.0; double dRes=gVar1.GetValue(d); PrintFormat("Double value: %0.15f",dRes); //--- float type float f=0.0; float fRes=gVar1.GetValue(f); PrintFormat("Float value: %0.7f",fRes); //--- string type string s=NULL; string sRes=gVar1.GetValue(s); PrintFormat("String value: %s",sRes); //--- Set a new value double new_val=3.191; if(gVar1.Value(new_val)) PrintFormat("New value is set: %f",new_val); //--- Set a new value on condition new_val=3.18; if(gVar1.ValueOnCondition(3.18,3.191)) PrintFormat("New value on conditionis set: %f",new_val); } }
グローバル変数は以下のように作成されます。
gVar1.Create("Gvar1",3.123456789101235,true)
最初の引数は後の変数名("Gvar1")の基本的なコンポーネントで、2番目の引数は(3.123456789101235)の値で、3番目の引数は変数が一時的(真)なものであることを示す特徴です。
変数名は名前とプログラムタイプを基本のコンポーネントに追加することで作成されます。
私の場合それは以下です。
- Gvar1 -基本コンポーネント
- prog_Globals_test1 -変数が作成されたプログラム(その名前はGlobals_test1です)
- プログラムタイプは- scr (スクリプト)です。
F3 を押すと、次のエントリが MetaTrader 5 ウィンドウのグローバル変数リストに表示されます。
図1 Test_temp_var1_prog_Globals_test1_scr 変数の値は 3.18
起動時、また正常に実装されたとき、『エキスパート』ジャーナルに以下のエントリーが表示されます。
KP 0 10:20:20.736 Globals_test1 (AUDUSD.e,H1) ---=== A new global var ===--- EH 0 10:20:21.095 Globals_test1 (AUDUSD.e,H1) Name: "Gvar1_temp_prog_Globals_test1_scr" LF 0 10:20:21.876 Globals_test1 (AUDUSD.e,H1) Is temporary: 1 MO 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) Double value: 3.123456789101235 KG 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) Float value: 3.1234567 OP 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) String value: 3.123456789101235 RH 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) New value is set: 3.191000 DJ 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) New value on conditionis set: 3.180000
変数値の異なるデータタイプはジャーナルに表示されます。
MetaTrader 5 ターミナルが再起動されると、Gvar1_temp_prog_Globals_test1_scr 変数はグローバル変数リストから消えます。それが起こるのは変数が一時的なものでそれはターミナルが開いているあいだ存続するためです。
MQL4/5 ではグローバル変数にデータを受け取る際、変数が一時的なものかどうか見つける方法はありません。おそらくもっとも簡単な特定方法は一時変数が変数名にキーを追加することです。たとえば、それは変数名の中にある接尾辞 "temp" の可能性があります。ただし、グローバル変数名作成を管理する必要があるのは、この方法の明確な欠点です。特にそのような変数が別のプログラムで作成されクラスを使用していないときには、顕著です。
ある時点で、私はグローバル変数がいくつ作成され、どれくらい速く作成されるのか知りたいと思いました。
わずかに前のスクリプトを変更し、それにGlobals_test2.mq5と名づけました。それは異なる実行回数で起動しました。変数を削除するため毎回実行後ターミナルの再起動を行いました。
#property script_show_inputs //--- #include "CGlobalVar.mqh" input uint InpCnt=10000; // Number of variables //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- start value uint start=GetTickCount(); //--- for(uint idx=0;idx<InpCnt;idx++) { CGlobalVar gVar; //--- Create a temporary global var if(!gVar.Create("Test_var"+IntegerToString(idx+1),idx+0.15,true)) Alert("Error creating a global variable!"); } //--- finish value uint time=GetTickCount()-start; //--- to print PrintFormat("Creation of %d global variables took %d ms",InpCnt,time); }
以下がその結果です(図2)。
図2 一時変数作成に要した時間
すべてのグローバル変数に対して行った同様のテストは図3に表示しています。その作成には長くかかることはありません。
その背後にある理由は、これら変数が Profiles フォルダ内のgvariables.dat ファイルにあるディスクに保存されるというものです。
図3 全グローバル変数作成に要した時間
それほどまで多くのグローバル変数を作成する必要はないと思います。私が行ったのは単に好奇心からです。
次の節ではグローバル変数のセットで作業しようと思います。
3. CGlobalVarList クラス
グローバル変数を使った作業を管理するには、 CGlobalVarListタイプのグローバル変数のクラスリストを作成します。このリストタイプは標準リストクラスCListの継承クラスです。
クラス宣言は以下のように記述します。
//+------------------------------------------------------------------+ //| Class CGlobalVarList | //+------------------------------------------------------------------+ class CGlobalVarList : public CList { //--- === Data members === --- private: ENUM_GVARS_TYPE m_gvars_type; //--- === Methods === --- public: //--- constructor/destructor void CGlobalVarList(void); void ~CGlobalVarList(void){}; //--- load/unload bool LoadCurrentGlobals(void); bool KillCurrentGlobals(void); //--- working with files virtual bool Save(const int _file_ha); virtual bool Load(const int _file_ha); //--- service void Print(const int _digs); void SetGvarType(const ENUM_GVARS_TYPE _gvar_type); //--- private: bool CheckGlobalVar(const string _var_name); };
現グローバル変数に接続しているオブジェクトは CGlobalVarListタイプのリストにインクルードされ、それからCGlobalVarList::LoadCurrentGlobals メソッドが使われます。
//+------------------------------------------------------------------+ //| Load current global vars | //+------------------------------------------------------------------+ bool CGlobalVarList::LoadCurrentGlobals(void) { ENUM_GVARS_TYPE curr_gvar_type=this.m_gvars_type; int gvars_cnt=GlobalVariablesTotal(); //--- for(int idx=0;idx<gvars_cnt;idx++) { string gvar_name=GlobalVariableName(idx); if(this.CheckGlobalVar(gvar_name)) continue; //--- gvar properties double gvar_val=GlobalVariableGet(gvar_name); datetime gvar_time=GlobalVariableTime(gvar_name); CGlobalVar *ptr_gvar=new CGlobalVar(gvar_name,gvar_val,gvar_time); //--- control gvar type if(CheckPointer(ptr_gvar)==POINTER_DYNAMIC) { if(curr_gvar_type>GVARS_TYPE_ALL) { bool is_temp=ptr_gvar.IsTemporary(); //--- only full-fledged if(curr_gvar_type==GVARS_TYPE_FULL) {if(is_temp)continue;} //--- only temporary else if(curr_gvar_type==GVARS_TYPE_TEMP) {if(!is_temp)continue;} } //--- try to add if(this.Add(ptr_gvar)>-1) continue; } //--- return false; } //--- return true; }
このメソッドは存在するグローバル変数をすべて読み、それをリストにインクルードします。
m_gvars_type 属性はインクルードされたグローバル変数タイプを管理します。それはENUM_GVARS_TYPE タイプの列挙です。
//+------------------------------------------------------------------+ //| Enumeration for gvars type | //+------------------------------------------------------------------+ enum ENUM_GVARS_TYPE { GVARS_TYPE_ALL=-1, // all global GVARS_TYPE_FULL=0, // only full GVARS_TYPE_TEMP=1, // only temporary };
CGlobalVarList リストの初期化前に、図4 にあるグローバル変数セットがあったとします。
図4 グローバル変数のおおよそのセット
このセットがリストによって正しく処理されるか確認していきます。その確認を実行するには Globals_test3.mq5 テストスクリプトを作成します。
#include "CGlobalVarList.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVarList gvarList; gvarList.LoadCurrentGlobals(); PrintFormat("Number of variables in the set: %d",gvarList.Total()); }
スクリプトが開始されたのち新しいグローバル変数(黄色で強調されています)が出現しました。それは起こるはずではありませんでした。(図5)。
図5 新規グローバル変数セット
文字列は次のように表示されます。
2014.10.21 11:35:00.839 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 10
それが起こったのは、CGlobalVarList::LoadCurrentGlobals メソッドの宣言で、CGlobalVar::Createメソッドへの参照があったためです。
それは新規グローバル変数が文字列内で作成されることを意味します。
if(ptr_gvar.Create(gvar_name,gvar_val))
また、グローバル変数のインデックスは新しい変数が出現すると変わります。それが混乱の源です。
私はCGlobalVar::Create メソッドをもっとアクティブでないものと置き換えることをお薦めします。リスト内で変数が配慮されるようCGlobalVar クラスにはパラメータを持つコンストラクタを追加する必要があります。
変更後、CGlobalVarList::LoadCurrentGlobalsメソッドは以下のように記述されます。
//+------------------------------------------------------------------+ //| Load current global vars | //+------------------------------------------------------------------+ bool CGlobalVarList::LoadCurrentGlobals(void) { int gvars_cnt=GlobalVariablesTotal(); //--- for(int idx=0;idx<gvars_cnt;idx++) { string gvar_name=GlobalVariableName(idx); double gvar_val=GlobalVariableGet(gvar_name); datetime gvar_time=GlobalVariableTime(gvar_name); CGlobalVar *ptr_gvar=new CGlobalVar(gvar_name,gvar_val,gvar_time); if(CheckPointer(ptr_gvar)==POINTER_DYNAMIC) if(this.Add(ptr_gvar)>-1) continue; //--- return false; } //--- return true; }
メソッド変更後、スクリプトは正しく動作しています。そして以下のレコードが表示されます。
2014.10.21 11:38:04.424 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 6
それから、リストを削除、表示できる機能を追加しようとしています。
以下が Globals_test3.mq5 スクリプトです。
//--- #include "CGlobalVarList.mqh" //--- input ENUM_GVARS_TYPE InpGvarType=GVARS_TYPE_FULL; // Set gvar type //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVarList gvarList; //--- delete gvars gvarList.SetGvarType(InpGvarType); //--- load current gvars gvarList.LoadCurrentGlobals(); Print("Print the list before deletion."); gvarList.Print(10); //--- delete gvars if(gvarList.KillCurrentGlobals()) { Print("Print the screen after deletion."); gvarList.Print(10); } }
異なるグローバル変数を10個作成してタスクを複雑にしようと思います(図6)。
図6 多様なグローバル変数
われわれのリスト gvarList には完全な変数のみインクルードします。それらは削除されます。
『エキスパート』のジャーナルには以下が入っています。
MG 0 11:05:01.113 Globals_test3 (AUDUSD.e,H1) Print the list before deletion. KL 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) OI 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) ---===Local list===--- QS 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Global variable type: GVARS_TYPE_FULL RI 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Total number of global variables: 10 EG 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Number of global variables in current list: 5 RN 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #1, name - gVar10_prog_test1_scr, value - 16.6400000000 KP 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #2, name - gVar2_prog_test1_scr, value - 4.6400000000 GR 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #3, name - gVar4_prog_test1_scr, value - 7.6400000000 RD 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #4, name - gVar6_prog_test1_scr, value - 10.6400000000 LJ 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #5, name - gVar8_prog_test1_scr, value - 13.6400000000 EH 0 11:06:18.675 Globals_test3 (AUDUSD.e,H1) Print the list after deletion. FS 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) JJ 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) ---===Local list===--- HN 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) Global variable type: GVARS_TYPE_FULL KH 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) Total number of global variables: 5 QP 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) Number of global variables in the current list: 0
完全なグローバル変数だけをインクルードするリストは正しく作成されました。
それはクリアされ、ターミナルには一時変数5個だけが残っていました(図7)。
図7 一時グローバル変数
意図したタスクは達成されました。
CGlobalVarList クラスには、ファイルにデータを保存するメソッドとファイルからデータをダウンロードするメソッドも実装されました。
4. 実用的アプリケーション
周知のようにMQL4/5 iは特殊化されたプログラム言語です。それはトレーディング戦略をプログラムするために作成されました。特定のトレーディングアイデアを公式化するための方法として言語ツールがすべて考慮されるのはそのためです。
Expert Advisorを MQL5 プラットフォームのグローバル変数にリンクする例は十分あります。今日私が提案するのは、プログラム実装の管理が必要な状況を詳しく調べることです。
モジュールアプローチに基づく売買ロボット"Globals_test_EA"のコードがあるとします。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Comment(""); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { Main(); }
ここでメインモジュールは次のように記述されます。
//+------------------------------------------------------------------+ //| Main module | //+------------------------------------------------------------------+ void Main(void) { //--- set flags for all modules for(int idx=0;idx<GVARS_LIST_SIZE;idx++) SetFlag(idx,false); //--- Check the trade possibility and connectivity //--- permission to trade if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) //--- connection to the trading server if(TerminalInfoInteger(TERMINAL_CONNECTED)) //--- permission to trade for the launched EA if(MQLInfoInteger(MQL_TRADE_ALLOWED)) { //--- 1) opening module Open(); //--- 2) closing module Close(); //--- 3) Trailing Stop module Trail(); } }
それは成分が3つあるメインモジュールです。
- オープンモジュール
- クローズモジュール
- トレーリングストップモジュール
ここでプログラム実行段階を制御するグローバル変数を作成する必要があります。
モジュールの形式には3段階あります。各段階では、2つのコントロールポイントが使用されます。第1のコントロールポイントはモジュール動作の初めを管理するもので、第2のコントロールポイントはモジュール動作の終わりを管理するものです。
コントロールポイントはグローバル変数の形式で実装されます。
よって、次の名前のグローバル変数が6個必要です。
//--- global variables: names string gVar_names[6]= { "gvarOpen_start","gvarOpen_finish", "gvarClose_start","gvarClose_finish", "gvarTrail_start","gvarTrail_finish" };
モジュールすべてに対してのフラグは Main() 関数の最初に設定され、個別のモジュールすべてでクリアされます。『独自』フラグについてのみ述べているのは言うまでもありません。たとえば、Open() モジュールを参照します。
//+------------------------------------------------------------------+ //| Open module | //+------------------------------------------------------------------+ void Open(void) { Comment(curr_module+__FUNCTION__); //--- if(!IsStopped()) { //--- clear the module start flag SetFlag(0,true); //--- assume that the module operates for approximately 1.25 s { Sleep(1250); } //--- clear the module finish flag SetFlag(1,true); } }
モジュール実行時には、プログラムが Open()ブロックで動作しているというコメントがチャートウィンドウに表示さます。
プログラムが強制終了されなければ、コントロールは対応フラグを設定/クリアする関数に渡されます。任意のコントロールポイントでフラグがクリアに失敗すると、モジュールは作業が終了していないとみなします。
グローバル変数によるモジュール動作の追跡段階のパターンは図8に表示されています。
図8 フラグシーケンス処理パターン
たとえば"Globals_test_EA" Expert Advisor はチャートにアタッチされ、正常に処理をしています。
チャートから Expert Advisor を削除したとき、ジャーナルに次のようなエントリが表示されました。
2014.10.22 20:14:29.575 Globals_test_EA (EURUSD.e,H1) Program forced to terminate before execution: <<Open_finish>>
そのためOpen(). モジュール内で終了が起こりました。
F3 を押してグローバル変数リストを開きます(図9)。
図9 "Globals_test_EA" Expert Advisor のグローバル変数
リストを見ると Open() モジュール動作の開始に関わるフラグだけがゼロ設定となっていました。
ポジションのオープン、クローズ、管理と連結するコマンド実行失敗時に潜在的に誤りが検出されているように見えます。
同じチャート上でロボットを再起動したあと、以下の情報がジャーナルに表示されます。
RQ 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Open_finish>> CL 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Close_start>> DH 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Close_finish>> ES 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Trail_start>> RS 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Trail_finish>>
このように、プログラム段階の失敗については警告を受け取ります。それはもうひとつ別の疑問に導きます。これら段階が失敗した場合、何を行うことができるのでしょうか?それはまたの機会に。
おわりに
本稿はターミナルのグローバル変数の処理を助けるオブジェクト作成のための MQL5 言語の オブジェクト指向機能を明らかにしました。
プログラム段階の実装のためのコントロールポイントとしてグローバル変数が使用される場合が例としての役割をしました。
いつものように、コメント、提案、建設的批判をお待ちしています。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/1210
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索