介绍
在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_est1.mq5,它将使用globalvar对象类型。
#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”),第二个参数是值(3.123456789101235),第三个参数表示变量是临时的(真)。
变量名是通过向基本组件添加程序名和类型来创建的。
我的情况是:
- 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”附加到变量名。然而,以这种方式创建全局变量名有一个明显的缺点,特别是那些使用其他程序而不是cglobalvar类创建的变量名。
在某种程度上,我想知道创建全局变量的数量和速度。
我稍微修改了前面的脚本,并将其命名为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所示。创建它们不需要很长时间。
这背后的原因是这些变量被保存到gvariables中。配置文件文件夹中磁盘上的DAT文件。
传说。三。创建完整全局变量的时间开销
我认为没有必要创建这么多的全局变量。我做这个评估是出于好奇。
在下一个里程中,我们将使用一组全局变量。
三。cglobalwarlist类
为了计划和处理全局变量,我们需要创建一个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); };
如果连接到当前全局变量的对象包含在cGlobalList类型列表中,则使用cGlobalList::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属性控制包含全局变量的类型。它是枚举“枚举”类型的枚举:
//+------------------------------------------------------------------+ //| 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 };
假设在初始化cglobalwarlist列表之前,有一组全局变量,如图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
发生这种情况是因为在cgloballist::loadcurrentglobals方法的声明中引用了cglobalvar::create方法。
这意味着新的全局变量将在此语句中创建:
if(ptr_gvar.Create(gvar_name,gvar_val))
此外,全局变量索引会随着新变量的出现而变化。这就是造成混乱的原因。
我建议使用低活动的方法来代替cglobalvar::create。必须将带有参数的构造函数添加到cglobalvar类中,以便可以在列表中访问变量。
修改后的cGlobalList::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。多变量全局变量
只有完整的变量包含在我们的列表中。然后它们将被删除。
“专家”日志包括以下内容:
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
此列表仅包含完全正确创建的全局变量。
然后清除,只留下五个临时变量(图例)。7)在终端。
传说。7。临时全局变量
计划完成的任务。
在cglobalwarlist类中,实现了将数据保存到文件和从文件加载数据的方法。
4。实际应用
众所周知,MQL4/5是一种特殊的编程语言。它用于创建程序性交易策略。这就是为什么语言中的所有工具都被认为是将某些交易想法形式化的原因。
在MQL5平台上有足够多的例程将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(); } }
主模块由三个部分组成:
- 开放式仓库模块;
- 仓库关闭模块;
- 遵循停止模块。
现在,我们需要为程序执行的阶段控制创建全局变量。
模块的组成分为三个阶段。每个阶段有两个控制点。第一点控制模块开始工作,第二点控制模块结束工作。
控制点以全局变量的形式实现。
所以我们需要六个全局变量,命名如下:
//--- global variables: names string gVar_names[6]= { "gvarOpen_start","gvarOpen_finish", "gvarClose_start","gvarClose_finish", "gvarTrail_start","gvarTrail_finish" };
所有模块的标志都设置在主()功能的开头,并在每个单独的模块中清除。不用说,我们所说的是“自己的”标志。例如,让我们参考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”ea加载到图表中并正常工作。
当我从图表中删除EA时,日志中会出现以下条目:
2014.10.22 20:14:29.575 Globals_test_EA (EURUSD.e,H1) Program forced to terminate before execution: <<Open_finish>>
因此,中断EA位于打开()模块中。
按F3键打开全局变量列表(图9)。
图9。“globals_test_ea”ea的全局变量
通过查看列表,只有负责启动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 Software Corp.翻译自俄语原文
,网址为:https://www.mql5.com/ru/articles/1210。
MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。