向指标或者EA中快速添加控制面板
使用图形面板
你的MQL4/MQL5指标或EA可能是世界上最有效的但是它仍旧有改进的空间。在大多数情况下,你需要进入程序设置来改变其输入参数。然而,这一步可以绕过去。
基于标准类库来开发你自己的控制面板。这将允许您更改设置而无需重新启动程序。此外,这将使你的程序更具吸引力,让它从竞争对手中脱颖而出。您可以在市场中浏览多种图形面板。
在本文中,我将向你展示如何向您的MQL4/MQL5程序添加简易面板。您还将了解到如何让程序读取输入参数并对它们的改变进行响应。
1. 将指标和面板结合起来
1.1. 指标
NewBar.mq5指标执行一个单一的操作。当新的柱形来到时在终端的EA日志中打印一条消息。指标代码如下:
//+------------------------------------------------------------------+ //| NewBar.mq5 | //| Copyright 2015, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2015, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property description "The indicator identifies a new bar" #property indicator_chart_window #property indicator_plots 0 //+------------------------------------------------------------------+ //| 自定义指标初始化函数 //+------------------------------------------------------------------+ int OnInit() { //--- 指标缓存映射 //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| 自定义指标迭代函数 //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { static datetime prev_time; //--- 转置time[]数组的访问顺序 - 就像在时间系列中的一样 ArraySetAsSeries(time,true); //--- 首次计算或者已经改变的柱形数量 if(prev_calculated==0)// 首次计算 { prev_time=time[0]; return(rates_total); } //--- if(time[0]>prev_time) Print("New bar!"); //--- prev_time=time[0]; //---返回prev_calculated的值用于下次调用 return(rates_total); } //+------------------------------------------------------------------+
现在我们深入研究NewBar.mq5运作的一些细节。
prev_time static今天变量在OnCalculate()函数中声明。此变量保存time[0]开始时间。下一步,将time[0]开始时间和prev_time变量进行比较。换句话说,当前tick的time[0]开始时间和前一个tick的开始时间相比较。如果下述条件满足:
if(time[0]>prev_time)
那么认为这是一个新的柱形。
下面的例子详细显示了NewBar.mq5是如何检测新的柱形到来的:
图 1. 在指标中检测新的柱形
让我们考虑非常平静市场环境下的10个tick。
Ticks 1-3:索引为0的柱形的开始时间(time[0])等于存储在prev_time静态变量中的时间,意味着没有新的柱形来到。
Tick 4:新柱形的tick到来了。当进入OnCalculate()函数中,time[0]的柱形开始时间为(2015.12.01 00:02:00),而 prev_time变量仍旧存储着前一个tick所在柱形的开始时间(2015.12.01 00:01:00)。因此,当time[0]>prev_time条件满足时,我们检测到新的柱形到来了。在退出OnCalculate()之前,prev_time变量从time[0] (2015.12.01 00:02:00)中获得新的值。
Ticks 5-8:索引为0的柱形的开始时间(time[0])同存储在prev_time静态变量中的相等,也就是说不是新的柱形。
Tick 9:新柱形的tick到来了。当进入OnCalculate()函数时,time[0]的值为柱形开始时间(2015.12.01 00:03:00),而 prev_time变量仍旧存储着前一个tick所在柱形的开始时间(2015.12.01 00:02:00)。因此,当time[0]>prev_time条件满足时,我们检测到新的柱形到来了。在退出OnCalculate()之前,prev_time变量被赋值为time[0] (2015.12.01 00:03:00)。
Tick 10:索引为0的柱形的开始时间(time[0])同存储在prev_time静态变量中的相等,也就是说不是新的柱形
1.2. 面板
所有面板的绘图参数(数量,尺寸以及控件元素的坐标)都汇聚在一个单一的include文件 PanelDialog.mqh中,它是一个面板实现类。
面板如下:
图 2. 面板
PanelDialog.mqh包含文件的代码如下:
//+------------------------------------------------------------------+ //| PanelDialog.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include <Controls\Dialog.mqh> #include <Controls\CheckGroup.mqh> //+------------------------------------------------------------------+ //| 定义 //+------------------------------------------------------------------+ //--- 缩进和间隔 #define INDENT_LEFT (11) // 左间距(留出边界宽度) #define INDENT_TOP (11) // 顶间距(留出边界宽度) #define INDENT_BOTTOM (11) // 上边距(留出边界宽度) //--- 按钮 #define BUTTON_WIDTH (100) // X坐标的尺寸 //+------------------------------------------------------------------+ //| CControlsDialog 类 //| 用法:控件应用的主对话框 //+------------------------------------------------------------------+ class CControlsDialog : public CAppDialog { private: CCheckGroup m_check_group; // CCheckGroup对象 public: CControlsDialog(void); ~CControlsDialog(void); //--- 创建 virtual bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2); //--- 图表事件处理函数 virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); protected: //--- 创建独立控件 bool CreateCheckGroup(void); //--- 独立控件事件处理函数 void OnChangeCheckGroup(void); }; //+------------------------------------------------------------------+ //| 事件处理 //+------------------------------------------------------------------+ EVENT_MAP_BEGIN(CControlsDialog) ON_EVENT(ON_CHANGE,m_check_group,OnChangeCheckGroup) EVENT_MAP_END(CAppDialog) //+------------------------------------------------------------------+ //| 构造函数 //+------------------------------------------------------------------+ CControlsDialog::CControlsDialog(void) { } //+------------------------------------------------------------------+ //| 析构函数 //+------------------------------------------------------------------+ CControlsDialog::~CControlsDialog(void) { } //+------------------------------------------------------------------+ //| 创建 //+------------------------------------------------------------------+ bool CControlsDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) { if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); //--- 创建独立控件 if(!CreateCheckGroup()) return(false); //--- 成功 return(true); } //+------------------------------------------------------------------+ //| 创建 "CheckGroup" 元素 //+------------------------------------------------------------------+ bool CControlsDialog::CreateCheckGroup(void) { //--- 坐标 int x1=INDENT_LEFT; int y1=INDENT_TOP; int x2=x1+BUTTON_WIDTH; int y2=ClientAreaHeight()-INDENT_BOTTOM; //--- 创建 if(!m_check_group.Create(m_chart_id,m_name+"CheckGroup",m_subwin,x1,y1,x2,y2)) return(false); if(!Add(m_check_group)) return(false); m_check_group.Alignment(WND_ALIGN_HEIGHT,0,y1,0,INDENT_BOTTOM); //--- 用字符填充 if(!m_check_group.AddItem("Mail",1<<0)) return(false); if(!m_check_group.AddItem("Push",1<<1)) return(false); if(!m_check_group.AddItem("Alert",1<<2)) return(false); Comment(__FUNCTION__+" : Value="+IntegerToString(m_check_group.Value())); //--- 成功 return(true); } //+------------------------------------------------------------------+ //| 事件处理 //+------------------------------------------------------------------+ void CControlsDialog::OnChangeCheckGroup(void) { Comment(__FUNCTION__+" : Value="+IntegerToString(m_check_group.Value())); } //+------------------------------------------------------------------+
正如你所看到的,我们的面板类不包含设置和读取独立固定开关状态的方法。
我们的目标是将NewBar.mq5作为主文件,添加输入参数,例如,能够选择新柱形出现时的报警方法(Mail,Push,或Alert)。另外,PanelDialog.mqh包含文件要含有用于设置和读取Mail,Push,或 Alert独立固定开关状态的方法。
1.3. 修改指标
注意:所有做出的修改都用颜色标记了。
首先,我们要实现PanelDialog.mqh包含文件:
#property indicator_chart_window #property indicator_plots 0 #include "PanelDialog.mqh" //+------------------------------------------------------------------+ //| 自定义指标初始化函数 //+------------------------------------------------------------------+ int OnInit()
然后添加输入参数:
#property indicator_chart_window #property indicator_plots 0 #include "PanelDialog.mqh" //--- 输入参数 input bool bln_mail=false; // 通过email通知 input bool bln_push=false; // 通过短信push通知 input bool bln_alert=true; // 通过alert通知 //+------------------------------------------------------------------+ //| 自定义指标初始化函数 //+------------------------------------------------------------------+ int OnInit()
编译指标(MetaEditor中按F7)并确定终端中输入参数显示正常:
图 3. 指标输入参数
1.4. 修改面板
现在,我们要添加用于设置和读取独立固定开关状态的Mail,Push和Alert方法到面板中。
让我们向面板类中添加新方法:
class CControlsDialog : public CAppDialog { private: CCheckGroup m_check_group; // CCheckGroup对象 public: CControlsDialog(void); ~CControlsDialog(void); //--- 创建 virtual bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2); //--- 图表事件处理函数 virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- 元素的SetCheck()函数 virtual bool SetCheck(const int idx,const int value); //--- 元素的GetCheck()函数 virtual int GetCheck(const int idx) const; protected: //--- 创建独立控件 bool CreateCheckGroup(void);
实现方法:
//+------------------------------------------------------------------+
//| 为元素设置检查
//+------------------------------------------------------------------+
bool CControlsDialog::SetCheck(const int idx,const bool check)
{
return(m_check_group.Check(idx,check));
}
//+------------------------------------------------------------------+
//| 获取元素的检查结果
//+------------------------------------------------------------------+
int CControlsDialog::GetCheck(const int idx)
{
return(m_check_group.Check(idx));
}
1.5. 将指标和面板结合的最后一步
在NewBar.mq5指标全局变量声明模块中声明面版类的变量
#property indicator_chart_window #property indicator_plots 0 #include "PanelDialog.mqh" //+------------------------------------------------------------------+ //| 全局变量 | //+------------------------------------------------------------------+ CControlsDialog ExtDialog; //--- 输入参数 input bool bln_mail=false; // 通过email通知 input bool bln_push=false; // 通过短信push通知 input bool bln_alert=true; // 通过alert通知
在最后添加OnChartEvent()函数:
//+------------------------------------------------------------------+
//| ChartEvent函数 |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
ExtDialog.ChartEvent(id,lparam,dparam,sparam);
}
在NewBar.mq5指标的OnInit()函数中创建面板,程序根据输入参数选择选项框:
int OnInit() { //--- 指标缓存映射 //--- 创建程序对话框 if(!ExtDialog.Create(0,"Notification",0,50,50,180,160)) return(INIT_FAILED); //--- 运行程序 if(!ExtDialog.Run()) return(INIT_FAILED); //--- ExtDialog.SetCheck(0,bln_mail); ExtDialog.SetCheck(1,bln_push); ExtDialog.SetCheck(2,bln_alert); //--- return(INIT_SUCCEEDED); }
这样我们就将指标和面板结合起来了。我们已经实现了确定一个选项框状态的方法 – 选中/释放(SetCheck),以及接收它的方法 (GetCheck)。
2. 将EA和面板结合
2.1. EA
让我们使用标准的EA...\MQL5\Experts\Examples\MACD\MACD Sample.mq5,作为样例。
2.2. 面板
最终的PanelDialog2.mqh面板看上去如下:
图 4. 面板2
将MACD Sample.mq5EA和PanelDialog2.mqh面板结合的好处是什么?这允许我们快速的修改EA参数(Lots,Trailing Stop Level (in pips),及其他),以及加载在当前时间框架上的EA的事件发生通知方式(Mail, Push, 和 Alert)。
被修改EA的参数(Lots,Trailing Stop Level (in pips),及其他)在点击Apply changes按钮后生效。 交易事件通知设置的改变(Mail,Push,和Alert)自动生效。没有必要按Apply changes按钮。
2.3. EA和面板应该有交互
图. 5. EA和面板之间的交互
加载后,EA应该将它的参数传递给面板。在点击Apply changes按钮并改变其参数之后,面板应向EA返回改变后的参数,并用新参数进行初始化。
2.4. 第一步。修改EA
将标准样例EA ...\MQL5\Experts\Examples\MACD\MACD Sample.mq5复制到你的文件夹中。例如,你可以创建Notification文件夹并将EA复制到其中:
图. 6. 创建一个新的文件夹
在EA的全局变量区域(不要和终端全局变量混淆),声明定义发送EA交易活动通知方法的新变量。请注意这些变量具有Inp前缀,就像其他外部变量一样:
#include <Trade\PositionInfo.mqh> #include <Trade\AccountInfo.mqh> //--- 输入参数 input bool InpMail=false; //通过email进行通知 input bool InpPush=false; // 通过短信push通知 input bool InpAlert=true; // 通过alert通知 //--- input double InpLots =0.1; // 交易量 input int InpTakeProfit =50; // 止赢(点数)
添加下面所有EA的外部变量的副本。副本前缀Ext:
input int InpMACDCloseLevel=2; // MACD水平(以点数计) input int InpMATrendPeriod =26; // MA周期 //--- ext 变量 bool ExtMail; bool ExtPush; bool ExtAlert; double ExtLots; int ExtTakeProfit; int ExtTrailingStop; int ExtMACDOpenLevel; int ExtMACDCloseLevel; int ExtMATrendPeriod; //--- int ExtTimeOut=10; // 交易操作之间的间隔时间(秒) //+------------------------------------------------------------------+ //| MACD 样例EA类 //+------------------------------------------------------------------+
使用OnInit()来复制外部变量:
//| EA初始化函数 //+------------------------------------------------------------------+ int OnInit(void) { ExtMail=InpMail; ExtPush=InpPush; ExtAlert=InpAlert; ExtLots=InpLots; ExtTakeProfit=InpTakeProfit; ExtTrailingStop=InpTrailingStop; ExtMACDOpenLevel=InpMACDOpenLevel; ExtMACDCloseLevel=InpMACDCloseLevel; ExtMATrendPeriod=InpMATrendPeriod; //--- 创建所有需要的对象 if(!ExtExpert.Init())
在这个阶段,带有Inp前缀的EA外部变量被使用在EA的CSampleExpert::InitIndicators,CSampleExpert::InitCheckParameters,和CSampleExpert::Init函数中。我们要用副本变量(带有Ext前缀的)替换这些函数中的外部变量。在此我建议一个非常规的解决方案:
替换完成后,编译该文件以确保所有这些过程都已正确完成。不要有任何错误。
2.5. 第二步。修改面板
图4所示面板是空的。既没有同EA进行“交互”的函数,也没有处理输入数据的函数。复制面板的空文件PanelDialog2Original.mqh到Notification文件夹下。
向面板类中添加外部变量。它们将用于存储所有输入数据的状态。注意mModification变量。我将在p中给出关于它的更多细节。2.7.
private: //--- 元素的GetCheck()函数 virtual int GetCheck(const int idx); //--- bool mMail; bool mPush; bool mAlert_; double mLots; // 交易量 int mTakeProfit; // 止赢(点数) int mTrailingStop; // 追踪止损水平(点数) int mMACDOpenLevel; // MACD开始水平(点数) int mMACDCloseLevel; //MACD结束水平(点数) int mMATrendPeriod; // MA 周期 //--- bool mModification; // 值是否改变 }; //+------------------------------------------------------------------+ //| 事件处理
在下面的面板类构造函数中初始化内部变量:
//+------------------------------------------------------------------+ //| 构造函数 //+------------------------------------------------------------------+ CControlsDialog::CControlsDialog(void) : mMail(false), mPush(false), mAlert_(true), mLots(0.1), mTakeProfit(50), mTrailingStop(30), mMACDOpenLevel(3), mMACDCloseLevel(2), mMATrendPeriod(26), mModification(false) { } //+------------------------------------------------------------------+ //| 析构函数
根据内部变量,向CControlsDialog::Create函数添加开关元素组:
if(!CreateButtonOK()) return(false); //--- SetCheck(0,mMail); SetCheck(1,mPush); SetCheck(2,mAlert_); //--- 成功 return(true); }
2.6. 第三步。修改EA
直到现在,EA和面板是两个相互独立的文件,彼此没有关联。让我们连结他们并声明面板的ExtDialog变量。
#include <Trade\PositionInfo.mqh> #include <Trade\AccountInfo.mqh> #include "PanelDialog2Original.mqh" //+------------------------------------------------------------------+ //| 全局变量 | //+------------------------------------------------------------------+ CControlsDialog ExtDialog; //--- 输入参数 input bool InpMail=false; //通过email进行通知 input bool InpPush=false; // 通过短信push通知
为了使得面板能运行和可见,要创建并加载它。此外,确保添加了OnChartEvent()(处理图表事件)和 OnDeinit() 函数。EA中的OnInit()函数看上去像这样:
int OnInit(void) { ExtMail=InpMail; ExtPush=InpPush; ExtAlert=InpAlert; ExtLots=InpLots; ExtTakeProfit=InpTakeProfit; ExtTrailingStop=InpTrailingStop; ExtMACDOpenLevel=InpMACDOpenLevel; ExtMACDCloseLevel=InpMACDCloseLevel; ExtMATrendPeriod=InpMATrendPeriod; //--- 创建所有需要的对象 if(!ExtExpert.Init()) return(INIT_FAILED); //--- 创建程序对话框 if(!ExtDialog.Create(0,"Notification",0,100,100,360,380)) return(INIT_FAILED); //--- 运行程序 if(!ExtDialog.Run()) return(INIT_FAILED); //--- 成功 return(INIT_SUCCEEDED); }
我们在OnDeinit()中销毁面板,OnDeinit()紧跟着OnInit():
//--- 成功 return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| EA反初始化函数 //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Comment(""); //--- 销毁对话框 ExtDialog.Destroy(reason); } //+------------------------------------------------------------------+ //| EA中处理新报价到来的函数 //+------------------------------------------------------------------+ void OnTick(void)
在EA的结尾添加OnChartEvent()函数(在OnTick函数之后)。
//--- 如果EA执行则改变超时的限制时间(秒) if(ExtExpert.Processing()) limit_time=TimeCurrent()+ExtTimeOut; } } } //+------------------------------------------------------------------+ //| ChartEvent函数 | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ExtDialog.ChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
现在,EA可以被编译并在图表上运行了。EA加载并带有面板。
图. 7. EA和面板
2.7. 第四步。修改面板。整合
首先加载EA然后,它的输入参数由用户定义。之后面板被加载。因此,面板应该含有同EA进行数据交互的功能。
让我们添加 Initialization() 方法,它用于接收参数并用接收的参数初始化面板内部变量。声明:
virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- 初始化 virtual bool Initialization(const bool Mail,const bool Push,const bool Alert_, const double Lots,const int TakeProfit, const int TrailingStop,const int MACDOpenLevel, const int MACDCloseLevel,const int MATrendPeriod); protected: //--- 创建独立控件 bool CreateCheckGroup(void);
方法体(在CControlsDialog::GetCheck前插入):
//+------------------------------------------------------------------+ //| 初始化 //+------------------------------------------------------------------+ bool CControlsDialog::Initialization(const bool Mail,const bool Push,const bool Alert_, const double Lots,const int TakeProfit, const int TrailingStop,const int MACDOpenLevel, const int MACDCloseLevel,const int MATrendPeriod) { mMail=Mail; mPush=Push; mAlert_=Alert_; mLots=Lots; mTakeProfit=TakeProfit; mTrailingStop=TrailingStop; mMACDOpenLevel=MACDOpenLevel; mMACDCloseLevel=MACDCloseLevel; mMATrendPeriod=MATrendPeriod; //--- return(true); } //+------------------------------------------------------------------+ //| 获取元素的检查结果 //+------------------------------------------------------------------+ int CControlsDialog::GetCheck(const int idx)
既然面板的内部变量已经被数据初始化,我们要正确的填充面板的控件元素(所有字段)。因为我们有六个输入字段,我将基于m_edit1提供一个样例。文本字符串看上去像这样:
... if(!m_edit1.Text("Edit1")) ...
但现在它看上去不一样了:
... if(!m_edit1.Text(DoubleToString(mLots,2))) ...
因此,每一个完整的字段对应一个特定的内部变量。
下一个方法为GetValues()返回内部变量的值:
virtual bool Initialization(const bool Mail,const bool Push,const bool Alert_, const double Lots,const int TakeProfit, const int TrailingStop,const int MACDOpenLevel, const int MACDCloseLevel,const int MATrendPeriod); //--- 获取值 virtual void GetValues(bool &Mail,bool &Push,bool &Alert_, double &Lots,int &TakeProfit, int &TrailingStop,int &MACDOpenLevel, int &MACDCloseLevel,int &MATrendPeriod); protected: //--- 创建独立控件 bool CreateCheckGroup(void);
在CControlsDialog::Initialization())之后插入方法体:
//+------------------------------------------------------------------+ //| 获取值 //+------------------------------------------------------------------+ void CControlsDialog::GetValues(bool &Mail,bool &Push,bool &Alert_, double &Lots,int &TakeProfit, int &TrailingStop,int &MACDOpenLevel, int &MACDCloseLevel,int &MATrendPeriod) { Mail=mMail; Push=mPush; Alert_=mAlert_; Lots=mLots; TakeProfit=mTakeProfit; TrailingStop=mTrailingStop; MACDOpenLevel=mMACDOpenLevel; MACDCloseLevel=mMACDCloseLevel; MATrendPeriod=mMATrendPeriod; } //+------------------------------------------------------------------+ //| 获取元素的检查结果 //+------------------------------------------------------------------+ int CControlsDialog::GetCheck(const int idx)
因为面板根据EA执行的任何交易动作发送与其对应的通知,应有一个特殊的方法来处理它。让我们来声明它:
virtual void GetValues(bool &Mail,bool &Push,bool &Alert_, double &Lots,int &TakeProfit, int &TrailingStop,int &MACDOpenLevel, int &MACDCloseLevel,int &MATrendPeriod); //--- 发送通知 virtual void Notifications(const string text); protected: //--- 创建独立控件 bool CreateCheckGroup(void);
在CControlsDialog::GetValues())之后插入方法体:
//+------------------------------------------------------------------+ //| 发送通知 //+------------------------------------------------------------------+ void CControlsDialog::Notifications(const string text) { int i=m_check_group.ControlsTotal(); if(GetCheck(0)) SendMail(" ",text); if(GetCheck(1)) SendNotification(text); if(GetCheck(2)) Alert(text); } //+------------------------------------------------------------------+ //| 获取元素的检查结果 //+------------------------------------------------------------------+ int CControlsDialog::GetCheck(const int idx)
mModification标识(在p. 2.5中提到的) 用于记忆面板中的参数是否被修改。
virtual void Notifications(const string text); //--- virtual bool Modification(void) const { return(mModification); } virtual void Modification(bool value) { mModification=value; } protected: //--- 创建独立控件 bool CreateCheckGroup(void);
在CControlsDialog::OnClickButtonOK中控制参数的修改,它用于处理按下Apply changes按钮的事件。
//+------------------------------------------------------------------+ //| 事件处理 //+------------------------------------------------------------------+ void CControlsDialog::OnClickButtonOK(void) { //--- 验证修改 if(m_check_group.Check(0)!=mMail) mModification=true; if(m_check_group.Check(1)!=mPush) mModification=true; if(m_check_group.Check(2)!=mAlert_) mModification=true; if(StringToDouble(m_edit1.Text())!=mLots) { mLots=StringToDouble(m_edit1.Text()); mModification=true; } if(StringToInteger(m_edit2.Text())!=mTakeProfit) { mTakeProfit=(int)StringToDouble(m_edit2.Text()); mModification=true; } if(StringToInteger(m_edit3.Text())!=mTrailingStop) { mTrailingStop=(int)StringToDouble(m_edit3.Text()); mModification=true; } if(StringToInteger(m_edit4.Text())!=mMACDOpenLevel) { mMACDOpenLevel=(int)StringToDouble(m_edit4.Text()); mModification=true; } if(StringToInteger(m_edit5.Text())!=mMACDCloseLevel) { mMACDCloseLevel=(int)StringToDouble(m_edit5.Text()); mModification=true; } if(StringToInteger(m_edit6.Text())!=mMATrendPeriod) { mMATrendPeriod=(int)StringToDouble(m_edit6.Text()); mModification=true; } }
此外,面板在处理程序中检查输入数据:
void OnChangeCheckGroup(void); void OnChangeEdit1(void); void OnChangeEdit2(void); void OnChangeEdit3(void); void OnChangeEdit4(void); void OnChangeEdit5(void); void OnChangeEdit6(void); void OnClickButtonOK(void);
我将忽略他们的描述。
2.8. 第五步。更改EA。最后的编辑
当前,面板是不能在策略测试器中运行的,因此我们需要实现保护并引入内部变量 – bool_tester标识。
//--- int ExtTimeOut=10; // 交易操作之间的间隔时间(秒) bool bool_tester=false; // true - 测试器模式 //+------------------------------------------------------------------+ //| MACD 样例EA类 //+------------------------------------------------------------------+ class CSampleExpert
向OnInit()中插入更改 – 防止在策略测试器中加载。在可视化前初始化面板的参数:
//--- 创建所有需要的对象 if(!ExtExpert.Init()) return(INIT_FAILED); //--- if(!MQLInfoInteger(MQL_TESTER)) { bool_tester=false; //--- ExtDialog.Initialization(ExtMail,ExtPush,ExtAlert, ExtLots,ExtTakeProfit,ExtTrailingStop, ExtMACDOpenLevel,ExtMACDCloseLevel,ExtMATrendPeriod); //--- 创建应用程序对话框 if(!ExtDialog.Create(0,"Notification",0,100,100,360,380)) return(INIT_FAILED); //--- 运行程序 if(!ExtDialog.Run()) return(INIT_FAILED); } else bool_tester=true; //--- 成功 return(INIT_SUCCEEDED); }
检查面板参数是否在OnChartEvent()中改变。如果是,EA要用新的参数初始化:
//+------------------------------------------------------------------+ //| ChartEvent函数 | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ExtDialog.ChartEvent(id,lparam,dparam,sparam); // 查询面板中的bool变量,参数是否已经改变 // 如果是,查询面板参数并调用 // CSampleExpert::Init(void) if(ExtDialog.Modification()) { ExtDialog.GetValues(ExtMail,ExtPush,ExtAlert, ExtLots,ExtTakeProfit,ExtTrailingStop, ExtMACDOpenLevel,ExtMACDCloseLevel,ExtMATrendPeriod); if(ExtExpert.Init()) { ExtDialog.Modification(false); Print("Parameters changed, ",ExtLots,", ",ExtTakeProfit,", ",ExtTrailingStop,", ", ExtMACDOpenLevel,", ",ExtMACDCloseLevel,", ",ExtMATrendPeriod); } else { ExtDialog.Modification(false); Print("Parameter change error"); } } } //+------------------------------------------------------------------+
总结
将面板和指标结合变得非常容易了。要做到这一点,我们已经在面板类中实现了完整的功能(控制元素的尺寸和位置,对事件的响应),并声明了面板类的变量以及在指标中添加了OnChartEvent()函数。
将EA和更加复杂的面板结合起来更具挑战,主要是因为需要组织EA和面板之间的“通信”。问题的复杂性主要在取决于面板是否做好通信的准备。换句话说,如果面板一开始就具有用于同其他程序集成的功能函数,那将会更易于将其同其他应用相结合(指标或EA)。
以下文件附到文本结尾:
- NewBarOriginal.mq5 — 初始指标文件。
- PanelDialogOriginal.mqh — 初始面板文件。
- NewBar.mq5 — 修改后的指标文件。
- PanelDialog.mqh — 修改后的面板文件。
- PanelDialog2Original.mqh — 第二个初始面板文件。
- PanelDialog2.mqh — 修改后的第二个面板文件。
- MACD Sample.mq5 — 修改后的EA文件。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/2171
"prev_time static今天变量在OnCalculate()函数中声明"
打字错误了,不是今天变量是静态变量。