介绍
在本文中,我打算描述并实践使用onchartevent()来处理由MQL5开发人员预定义的典型(标准)事件。处理器例程已包含在MQL5文章和代码库中。
然而,我的目标是在面向事件编程(EOP)的环境中分析性能。我相信这种处理器可以成功地应用于自动和半自动交易系统。
1。ChartEvent事件
那么,让我们先看看什么是事件类型。
根据文档,ChartEvent事件发生在图表工作时,尤其是在以下情况下:
- 按“激活图表”窗口中键盘上的键。
- 创建图形对象。
- 删除图形对象。
- 单击图形对象。
- 用鼠标拖动图形对象。
- 在Labeledit图形对象的文本框中完成文本编辑。
所以这个事件可以和图表交互。此外,这种交互可能是手动事务或某些算法操作(自动事务)的结果。
mql5开发人员可以按照枚举图表事件枚举中指定的类型对ChartEvent事件进行分类。
需要注意的是,这个列表有一系列用户定义的事件,它们作为程序员的隐藏保留。MQL5开发人员可以为其定制的事件提供65535个标识符。
为了处理自定义事件,一个特殊的生成器函数eventChartCustom()可以满足程序员的需要。尽管如此,本文不考虑定制事件。
2。图表事件处理器和生成器
所有ChartEvent事件都通过一个特殊函数OnChartEvent()进行处理。这与mql5的概念一致,例如,trade事件由on trade()函数处理,init事件由on init()函数处理,等等。
onChartEvent()函数具有以下签名:
void OnChartEvent(const int id, // event identifier const long& lparam, // parameter of the event of type long const double& dparam, // parameter of the event of type double const string& sparam // parameter of the event of type string )
所有输入参数都是常量,在调用处理器时传递有用的信息。
因此,id参数的值可以显示调用处理器的特殊事件。其他可能的数字类型包括长整数、双精度和字符串。通过这种方式可以获得更多的事件信息。
稍后,我们将创建一个指定参数值的例程来分析将发生什么。
ChartEvent事件的自定义部分由程序员实现,并连接到EventChartCustom()函数。此函数生成事件。功能如下:
bool EventChartCustom(long chart_id, // receiving chart identifier ushort custom_event_id, // event identifier long lparam, // the long parameter double dparam, // the double parameter string sparam // the string parameter )
实际上,生成器函数可以基于输入参数值创建事件,并将其发送到任何图表,包括当前图表。后一种类型是:无符号短整数、长整数、双精度、字符串。
onchartevent()和eventchartcomm()函数一起构成了一个强大的工具,这是面向事件编程优势的一个很好的例子。
三。标准事件处理模板
现在,我将查看图表事件的类型并给出每个示例。每个事件都有自己的EventProcessor版本。MQ5及其代码包括图表事件处理。在MQL5中有10个典型事件。
对于其中三个(鼠标事件、图形对象创建事件、图形对象删除事件),我们需要准备一个图表。您可以使用chartsetinteger()函数来完成此操作。它允许图表响应指定的事件。
响应事件处理的典型模块如下:
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { string comment="Last event: "; //--- select event on chart switch(id) { //--- 1 case CHARTEVENT_KEYDOWN: { comment+="1) keystroke"; break; } //--- 2 case CHARTEVENT_MOUSE_MOVE: { comment+="2) mouse"; break; } //--- 3 case CHARTEVENT_OBJECT_CREATE: { comment+="3) create graphical object"; break; } //--- 4 case CHARTEVENT_OBJECT_CHANGE: { comment+="4) change object properties via properties dialog"; break; } //--- 5 case CHARTEVENT_OBJECT_DELETE: { comment+="5) delete graphical object"; break; } //--- 6 case CHARTEVENT_CLICK: { comment+="6) mouse click on chart"; break; } //--- 7 case CHARTEVENT_OBJECT_CLICK: { comment+="7) mouse click on graphical object"; break; } //--- 8 case CHARTEVENT_OBJECT_DRAG: { comment+="8) move graphical object with mouse"; break; } //--- 9 case CHARTEVENT_OBJECT_ENDEDIT: { comment+="9) finish editing text"; break; } //--- 10 case CHARTEVENT_CHART_CHANGE: { comment+="10) modify chart"; break; } } //--- Comment(comment); }
在每个条件分支中,我添加一个字符串来描述所选事件。因此,在注释行中,我们可以看到图表上的最后一个事件。如果运行模板并在图表中执行各种操作,您将注意到注释行中可能有不同的记录。
显然,使用这个EA只能确定事件类型。我们需要扩大它的能力。
4。标准事件处理例程
4.1。键盘事件
第一个条件分支使用键盘按钮,因此我们的EA可以响应该按钮。当你按下“向上箭头”时,你买,当你按下“向下箭头”时,你卖。则条件分支如下:
//--- 1 case CHARTEVENT_KEYDOWN: { //--- "up" arrow if(lparam==38) TryToBuy(); //--- "down" arrow else if(lparam==40) TryToSell(); comment+="1) keystroke"; //--- break; }
有关TryToBuy()和TryToSell()实现的详细信息,请参阅随附的EA源代码。交易参数(手大小、止损、止损等)指定为输入变量(inPlot、inStopLoss、inTakeProfit等)。还应该提到参数lparam捕获了键的代码。
EA的更新版本称为eventprocessor 1.mq5。
4.2。鼠标事件
只有在图表中指定了属性图表“事件鼠标移动”时,才会处理此事件类型。因此,EA的初始化模块包括如下语句:
//--- mouse move bool is_mouse=false; if(InpIsEventMouseMove) is_mouse=true; ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,is_mouse);
应该注意的是,如果使用鼠标,那么自然地,鼠标事件将频繁发生。因此,有机会禁用事件处理非常有用。lparam和dparam处理器参数报告相应的x和y坐标。
我们将继续讨论日常事务。假设零列是从右边框偏移的。如果将屏幕上悬空的光标从偏移量向右滑动,将弹出一个窗口提示您购买或出售。
要做到这一点,我们必须首先确定偏移量。我们将继续使用输入变量来确定从右边框到零列的距离百分比。(输入图表移位大小)。
传说。1交易操作窗口
我们继续使用函数来启动偏移量并确定其大小,chartshiftset()和chartshiftsizeset()。然后,我们应该确定光标的X坐标是否在X坐标之前位于左边界,以及是否已移动到右边界。如果是这种情况,则会出现指示销售的窗口(图例)。1)。
实现这些目标集的代码如下:
//--- 2 case CHARTEVENT_MOUSE_MOVE: { comment+="2) mouse"; //--- if a mouse event is processed if(InpIsEventMouseMove) { long static last_mouse_x; //--- enable shift if(ChartShiftSet(true)) //--- set shift size if(ChartShiftSizeSet(InpChartShiftSize)) { //--- chart width int chart_width=ChartWidthInPixels(); //--- calculate X coordinate of shift border int chart_shift_x=(int)(chart_width-chart_width*InpChartShiftSize/100.); //--- border crossing condition if(lparam>chart_shift_x && last_mouse_x<chart_shift_x) { int res=MessageBox("Yes: buy / No: sell","Trade operation",MB_YESNO); //--- buy if(res==IDYES) TryToBuy(); //--- sell else if(res==IDNO) TryToSell(); } //--- store mouse X coordinate last_mouse_x=lparam; } } //--- break; }
买卖是通过先前创建的交易功能完成的。更新的版本ea命名为eventprocessor 2.mq5。
4.3。图形对象创建事件
此事件类型是在图形上创建图形对象时生成的。与鼠标事件类似,此类型要求通过属性图表“事件”对象“创建”授予权限。如果我们打算在新图形对象出现时进行响应,则需要在初始化模块中指定它一次。
//--- object create bool is_obj_create=false; if(InpIsEventObjectCreate) is_obj_create=true; ChartSetInteger(0,CHART_EVENT_OBJECT_CREATE,is_obj_create);
处理器中唯一的参数是要包含的信息。有一个字符串参数sparam,它保留所创建图形对象的名称。我们可以按名称查找对象,处理它们,然后决定下一步要做什么。
这是一个简单的程序。我们想在图表上画一条水平线,让自动交易以图表中所有列的最高价格放置它,然后画另外两条线。底线将被放在最低的价格,而第三行将位于两者之间的等距位置。
执行任务的代码:
//--- 3 case CHARTEVENT_OBJECT_CREATE: { comment+="3) create graphical object"; //--- if graphical object creation event is processed if(InpIsEventObjectCreate) { //--- capture creation of horizontal line int all_hor_lines=ObjectsTotal(0,0,OBJ_HLINE); //--- if this is the only line if(all_hor_lines==1) { string hor_line_name1=sparam; //--- calculate levels int visible_bars_num=ChartVisibleBars(); //--- arrays for high and low prices double highs[],lows[]; //--- int copied=CopyHigh(_Symbol,_Period,0,visible_bars_num-1,highs); if(copied!=visible_bars_num-1) { Print("Failed to copy highs!"); return; } copied=CopyLow(_Symbol,_Period,0,visible_bars_num-1,lows); if(copied!=visible_bars_num-1) { Print("Failed to copy lows!"); return; } //--- high and low prices double ch_high_pr,ch_low_pr,ch_mid_pr; //--- ch_high_pr=NormalizeDouble(highs[ArrayMaximum(highs)],_Digits); ch_low_pr=NormalizeDouble(lows[ArrayMinimum(lows)],_Digits); ch_mid_pr=NormalizeDouble((ch_high_pr+ch_low_pr)/2.,_Digits); //--- place created line on high if(ObjectFind(0,hor_line_name1)>-1) if(!ObjectMove(0,hor_line_name1,0,0,ch_high_pr)) { Print("Failed to move!"); return; } //--- create line on low string hor_line_name2="Hor_line_min"; //--- if(!ObjectCreate(0,hor_line_name2,OBJ_HLINE,0,0,ch_low_pr)) { Print("Failed to create the 2nd horizontal line!"); return; } //--- create line between high and low string hor_line_name3="Hor_line_mid"; //--- if(!ObjectCreate(0,hor_line_name3,OBJ_HLINE,0,0,ch_mid_pr)) { Print("Failed to create the 3rd horizontal line!"); return; } } } break; }
更新后的版本ea命名为eventprocessor 3.mq5。
传说。2。处理创建图形对象事件的结果
完成这个过程后,我得到了下面的图片(图例。2)。因此,集成功能使EA能够响应图形对象的创建并采取行动。
4.4。通过“属性”对话框修改图形对象属性事件
此事件类型与前一个事件类型非常相似。当通过“属性”对话框修改图形对象的属性时,会触发此事件。例如,此工具可能有助于同步同一类型对象的图形属性。
假设图表上有几个对象。交易者通常在图表上画很多线。这些行有时需要隐藏,但不能删除。我们会找到解决这个问题的办法。修改后的线条可以脱色,也可以为其他图形对象完成。之后,代码如下:
//--- 4 case CHARTEVENT_OBJECT_CHANGE: { comment+="4) change object properties via properties dialog"; //--- string curr_obj_name=sparam; //--- find the changed object if(ObjectFind(0,curr_obj_name)>-1) { //--- get object color color curr_obj_color=(color)ObjectGetInteger(0,curr_obj_name,OBJPROP_COLOR); //--- total number of objects on chart int all_other_objects=ObjectsTotal(0); //--- find other objects for(int obj_idx=0;obj_idx<all_other_objects;obj_idx++) { string other_obj_name=ObjectName(0,obj_idx); if(StringCompare(curr_obj_name,other_obj_name)!=0) if(!ObjectSetInteger(0,other_obj_name,OBJPROP_COLOR,curr_obj_color)) { Print("Failed to change the object color!"); return; } } //--- redraw chart ChartRedraw(); } //--- break;
假设图表上有一组线条(图例)。3)。
传说。三。多色动态线条
如果我们试图更改任何线条的颜色,或者更精确地说,请在“属性”对话框(图例)中对其进行脱色。4),图表上看不到任何线条。同时,图形对象仍然存在。
传说。4。修改线条颜色
EA的更新版本称为EventProcessor 4.mq5。
4.5。图形对象删除事件
正如此事件类型的名称所暗示的那样,当从关系图中删除对象时,它会出现。这是最后一组需要直接预授权才能处理的事件。这可以通过属性图表“事件”对象“删除”来完成。
//--- object delete bool is_obj_delete=false; if(InpIsEventObjectDelete) is_obj_delete=true; ChartSetInteger(0,CHART_EVENT_OBJECT_DELETE,is_obj_delete);
这里是另一个假设的例子。在EA加载的图形上,有一组不同类型的图形对象。假设我们需要删除特定类型的对象。例如,垂直线(图例。5)。
传说。5。五条垂直线和其他线
我们只需要删除一条垂直线,然后EA将删除其他(图例)。6)。
传说。6。残差线
以下条目将出现在“专家”横幅上:
NS 0 10:31:17.937 EventProcessor5 (EURUSD.e,W1) Vertical lines before removing: 4 MD 0 10:31:17.937 EventProcessor5 (EURUSD.e,W1) Vertical lines removed from the chart: 4 QJ 0 10:31:18.078 EventProcessor5 (EURUSD.e,W1) Vertical lines after removing: 0
必须提及的一个重要方面。一旦对象被删除,就不能再次访问其属性。这意味着,如果我们不预先恢复对象所需的数据,则在删除该对象后,将无法访问它。因此,如果我们想找出被删除对象的类型,必须在删除之前保存它。我建议MQL5开发人员在终端中创建可用图表的历史记录。这允许我们引用已删除对象的属性。
我们称最终的EA版本为EventProcessor 5.mq5。
4.6。图形上的鼠标单击事件
如果在图表中单击鼠标左键,则会发生此事件。右键单击图表打开菜单,中键单击弹出交叉光标。lparam和dparam处理器参数报告相应的x和y坐标。
下面的简单任务将作为一个例子。我们会安排在鼠标点击的地方画一个“购买”箭头。对象“arrow”只需要一个位置点。因此,只需将x和y坐标转换为定位点的时间和价格值。
上述示例的代码:
//--- 6 case CHARTEVENT_CLICK: { comment+="6) mouse click on chart"; //--- object counter static uint sign_obj_cnt; string buy_sign_name="buy_sign_"+IntegerToString(sign_obj_cnt+1); //--- coordinates int mouse_x=(int)lparam; int mouse_y=(int)dparam; //--- time and price datetime obj_time; double obj_price; int sub_window; //--- convert the X and Y coordinates to the time and price values if(ChartXYToTimePrice(0,mouse_x,mouse_y,sub_window,obj_time,obj_price)) { //--- create object if(!ObjectCreate(0,buy_sign_name,OBJ_ARROW_BUY,0,obj_time,obj_price)) { Print("Failed to create buy sign!"); return; } //--- redraw chart ChartRedraw(); //--- increase object counter sign_obj_cnt++; } //--- break; }
EA的当前版本名为EventProcessor 6.mq5。
4.7。鼠标单击图形对象事件
此类型的图表事件与前一个图表事件不同,仅在图形对象上单击鼠标时才保留。字符串参数sparam将包含click对象的名称。在上一个示例中,我们创建了“购买”箭头。让我们在单击此类型的对象时,将其转换为“sell”箭头。
处理器的模块代码如下:
//--- 7 case CHARTEVENT_OBJECT_CLICK: { comment+="7) mouse click on graphical object"; //--- string sign_name=sparam; //--- delete buy arrow if(ObjectDelete(0,sign_name)) { //--- redraw chart ChartRedraw(); //--- static uint sign_obj_cnt; string sell_sign_name="sell_sign_"+IntegerToString(sign_obj_cnt+1); //--- coordinates int mouse_x=(int)lparam; int mouse_y=(int)dparam; //--- time and price datetime obj_time; double obj_price; int sub_window; //--- convert the X and Y coordinates to the time and price values if(ChartXYToTimePrice(0,mouse_x,mouse_y,sub_window,obj_time,obj_price)) { //--- create object if(!ObjectCreate(0,sell_sign_name,OBJ_ARROW_SELL,0,obj_time,obj_price)) { Print("Failed to create sell sign!"); return; } //--- redraw chart ChartRedraw(); //--- increase object counter sign_obj_cnt++; } } //--- break; }
为了说明这个例子,我保持鼠标点击处理的条件分支不变。启动ea,左键单击鼠标三次,得到三个购买箭头(图例)。7)。我用黄色突出他们的位置。
传说。7、“买”箭
如果现在单击每个“购买”箭头,我们将得到以下图片(图例)。8)。
传说。8.买入和卖出箭头
“sell”箭头按计划出现,但没有出现“buy”设计箭头。这就是为什么我打开图表上的对象列表,用黄色突出显示名为“购买”的箭头。
很容易注意到EA创建了第四、第五和第六个“购买”箭头。为什么会这样?这是因为第一次单击对象会触发两个事件:第一个事件是实际单击对象,第二个事件是单击图形。最后一个事件生成创建“购买”箭头操作。在这种情况下,有必要补充该机制,以避免处理图表上的第二个单击事件。我认为时间控制可以作为一种机制来使用。
让我们添加一个全局变量glasttime。这将有助于控制“购买”箭头的创建时间。如果在创建“sell”箭头的250毫秒内调用click处理器,则忽略该调用。
在重新绘制图形之前,请将以下语句添加到处理对象单击模块中:
//--- store the moment of creation gLastTime=GetTickCount();
时间验证也应添加到处理图表单击模块中。
uint lastTime=GetTickCount(); if((lastTime-gLastTime)>250) { //--- click handling }
让我们再次在图表上创建三个“购买”箭头(图例。9)。
0
传说。9、“买”箭
我们试着点击它们,即使它们的尺寸很小。单击后,箭头变为“销售”类型(图例。10)。
1
传说。10、“射箭”
同样,我们将新版本的eventprocessor命名为7.mq5。
4.8。鼠标移动图形对象事件
当对象在图形区域内移动时,会发生此事件。处理器接收由字符串参数sparam传递的移动对象的名称。
这是另一个例子。日交易者通常在固定的时间间隔内交易。垂直线是时间间隔的边界。这幅画看起来像传说。11。将突出显示利息间隔。
2
传说。11。时间间隔边界
可以手动修改时间间隔。所以我们的半自动化将不得不处理这些变化。
在全局级别,我们将创建两个名为gtimelimit1_name和gtimelimit2_name的垂直变量。我们还需要为矩形创建一对变量,以使图表上的非事务周期变暗。还需要为全局变量创建位置点。因为我们有两个矩形,所以我们需要四个定位点。
条件分支图表事件对象拖动处理器的代码:
//--- 8 case CHARTEVENT_OBJECT_DRAG: { comment+="8) move graphical object with mouse"; string curr_obj_name=sparam; //--- if one of the vertical lines is moved if(!StringCompare(curr_obj_name,gTimeLimit1_name) || !StringCompare(curr_obj_name,gTimeLimit2_name)) { //--- the time coordinate of vertical lines datetime time_limit1=0; datetime time_limit2=0; //--- find the first vertical line if(ObjectFind(0,gTimeLimit1_name)>-1) time_limit1=(datetime)ObjectGetInteger(0,gTimeLimit1_name,OBJPROP_TIME); //--- find the second vertical line if(ObjectFind(0,gTimeLimit2_name)>-1) time_limit2=(datetime)ObjectGetInteger(0,gTimeLimit2_name,OBJPROP_TIME); //--- if vertical lines are found if(time_limit1>0 && time_limit2>0) if(time_limit1<time_limit2) { //--- update properties of rectangles datetime start_time=time_limit1; datetime finish_time=time_limit2; //--- if(RefreshRecPoints(start_time,finish_time)) { //--- if(!ObjectMove(0,gRectLimit1_name,0,gRec1_time1,gRec1_pr1)) { Print("Failed to move the 1st point!"); return; } if(!ObjectMove(0,gRectLimit1_name,1,gRec1_time2,gRec1_pr2)) { Print("Failed to move the 2nd point!"); return; } //--- if(!ObjectMove(0,gRectLimit2_name,0,gRec2_time1,gRec2_pr1)) { Print("Failed to move the 1st point!"); return; } if(!ObjectMove(0,gRectLimit2_name,1,gRec2_time2,gRec2_pr2)) { Print("Failed to move the 2nd point!"); return; } } } } //--- break; }
此代码包含自定义函数refreshRecpoints()。它处理两个矩形定位点的数字更新。EA的初始块可以提供有关创建图形对象的信息。EA的更新版本称为EventProcessor 8.mq5。
4.9。在文本框中完成文本编辑事件
此事件类型具有非常专业的性质,并在数据输入域中编辑文本时显示。参数sparam包含工作对象的名称。
让我们考虑一个例子。在数据输入字段中,我们输入要执行的事务操作。只有两种操作——买卖。如果我们在输入字段中输入“购买”,EA将购买资产,如果我们输入“出售”,资产将被出售。我们将安排该字段不区分大小写,也就是说,我们可以输入“买入”和“卖出”。文本和输入字段在销售时为红色,在采购时为蓝色(图例)。12)。
3
传说。12。购买文本域
条件分支ChartEvent_Object_EndEdit的代码:
//--- 9 case CHARTEVENT_OBJECT_ENDEDIT: { comment+="9) end of editing a text in the data entry field"; //--- string curr_obj_name=sparam; //--- if specified text field is being edited if(!StringCompare(curr_obj_name,gEdit_name)) { //--- get object description string obj_text=NULL; if(ObjectGetString(0,curr_obj_name,OBJPROP_TEXT,0,obj_text)) { //--- check value if(!StringCompare(obj_text,"Buy",false)) { if(TryToBuy()) //--- set text color ObjectSetInteger(0,gEdit_name,OBJPROP_COLOR,clrBlue); } else if(!StringCompare(obj_text,"Sell",false)) { if(TryToSell()) //--- set text color ObjectSetInteger(0,gEdit_name,OBJPROP_COLOR,clrRed); } else { //--- set text color ObjectSetInteger(0,gEdit_name,OBJPROP_COLOR,clrGray); } //--- redraw chart ChartRedraw(); } } //--- break; }
EA的更新版本称为EventProcessor9.mq5。您可以在原始文件中找到创建文本字段的模块。
4.10。图表修改事件
在本文中我们将看到的最后一个事件是修改图表设置。这是一个奇怪的事件,因为此时我们正在处理图表本身,而不是图表上的对象。开发人员说,当图形大小改变或新设置生效时,就会发生此事件。
这是另一个例子。假设禁止更改图表设置。在此限制下,将忽略所有修改设置的尝试。实际上,EA只会返回先前的值。让我们修改图表的以下参数:
- 显示网格;
- 图表显示类型;
- 背景色。
条件分支代码:
//--- 10 case CHARTEVENT_CHART_CHANGE: { //--- current height and width of the chart int curr_ch_height=ChartHeightInPixelsGet(); int curr_ch_width=ChartWidthInPixels(); //--- if chart height and width have not changed if(gChartHeight==curr_ch_height && gChartWidth==curr_ch_width) { //--- fix the properties: //--- display grid if(!ChartShowGridSet(InpToShowGrid)) { Print("Failed to show grid!"); return; } //--- type of chart display if(!ChartModeSet(InpMode)) { Print("Failed to set mode!"); return; } //--- background color if(!ChartBackColorSet(InpBackColor)) { Print("Failed to set background сolor!"); return; } } //--- store window dimensions else { gChartHeight=curr_ch_height; gChartWidth=curr_ch_width; } //--- comment+="10) modify chart"; //--- break; }
EA的最终版本称为EventProcessor 10.mq5。
结论
在本文中,我试图描述MetaTrader 5中典型图表事件的多样性。我希望这些事件处理例程将帮助开始编写MQL5代码的程序员。
本文由MetaQuotes Software Corp.翻译自俄语原文
,网址为https://www.mql5.com/ru/articles/689。
MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。