
向指标或者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
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.




我知道,但也许你能帮我!
我帮不了你--我早就不支持旧终端了。
#include <Controls\Panel.mqh>
#include <Controls\Edit.mqh>
// #include <Controls\Defines.mqh>
#include <Controls\Button.mqh>
关于交易、自动交易系统和测试交易策略的论坛
为什么更新智能交易系统设置时面板会移动?
Andrey Khatimlianskii, 2016.03.10 13:17
大概是这样
"prev_time static今天变量在OnCalculate()函数中声明"
打字错误了,不是今天变量是静态变量。