我想提请读者注意以下几点。在当前版本的MQL5中,客户机终端中有14个正式的事件处理器。此外,程序员可以使用EventChartCustom()创建自定义事件,使用OnChartEvent()处理这些事件。但是,文档中没有提到“事件驱动编程”(EDP)。这很奇怪。结果表明,MQL5中的任何程序都是基于EDP原则的。例如,所有EA模板中的“EA事件处理器”步骤允许用户选择。
显然,无论如何,在MQL5中都使用了事件驱动的编程机制。语言中的程序块由两部分组成:事件选择和处理。此外,如果我们谈论的是客户机事件,程序员只控制第二部分,即事件处理程序。为了公平起见,也有一些异常事件。还包括计时器和自定义事件。对这些事件的控制权完全留给程序员。
在深入讨论我们的主题之前,让我们先参考官方资料。
根据文档,TradeTransaction事件是事务帐户确定操作的结果。操作本身包含几个确定性事务阶段。例如,当以市场价格开仓时,通过交易账户进行的最常见操作包括以下几个阶段:
这些序列只显示终端和服务器对之间的逻辑,这些逻辑反映在EA代码的字符串中。从贸易交易事件的角度来看,在市场中开仓的方式如下:
因此,OnTradeTransaction()处理器在仓库打开时被调用五次。
稍后我们将详细讨论程序代码,现在我们将仔细查看函数头。它有三个输入参数。
voidOnTradeTransaction(constMqlTradeTransaction&trans,//structureofthetradetransactionconstMqlTradeRequest&request,//structureoftherequestconstMqlTradeResult&result//structureoftheresponse);
这些参数在文档中有详细描述。我想引起注意的是,事务结构的参数是一类抛出消息,在当前调用过程中由处理器接收。
关于交易我也要说几句,因为我们会遇到很多。
在mql5中,枚举交易类型是提供交易类型的特殊枚举。为了找出交易公司的类型,我们需要参考参数mqltradetransactiontype常量。
structMqlTradeTransaction{ulongdeal;//Ticketofthedealulongorder;//Ticketoftheorderstringsymbol;//TradesymbolENUM_TRADE_TRANSACTION_TYPEtype;//TypeofthetradetransactionENUM_ORDER_TYPEorder_type;//TypeoftheorderENUM_ORDER_STATEorder_state;//StatusoftheorderENUM_DEAL_TYPEdeal_type;//TypeofthedealENUM_ORDER_TYPE_TIMEtime_type;//Orderexpirationtypedatetimetime_expiration;//Orderexpirationtimedoubleprice;//Pricedoubleprice_trigger;//PricethattriggerstheStopLimitorderdoubleprice_sl;//LevelofStopLossdoubleprice_tp;//LevelofTakeProfitdoublevolume;//Volumeinlots};
结构的第四个字段是我们要查找的枚举变量。
本质上,所有事务的定位都会导致对OnTradeTransaction()处理器的五次调用。其中有:
修改仓库是唯一两次调用TradeTransaction处理器的事务操作。
由于没有与清晰事务操作对应的事务类型的信息,因此我们打算通过实验和调试来发现它。
在此之前,我们需要创建一个包含TradeTransaction事件处理程序的EA模板。我把模板命名为TradeProcessor。MQ5。我添加了一个功能来显示日志中结构字段值的信息。这些值是事件处理程序的参数。分析这些记录将非常耗时,但最终它将通过呈现事件的全景来进行补偿。
我们需要在MetaTrader5终端的任何图表上以调试模式启动EA。
手动打开仓库并查看代码。这是第一次处理器调用的情况(图例)。1)。
传说。1。类型字段等于交易请求
日志中显示以下条目:
在此块中,我们只对事务类型的记录感兴趣。如我们所见,这种类型属于(交易请求)请求。
可以在请求块中获取请求的详细信息。
请求执行的结果数据可以从响应块中获得。
KG017:37:53.233TradeProcessor(EURUSD,H1)---===Response===---JR017:37:53.233TradeProcessor(EURUSD,H1)Codeoftheoperationresult:10009GD017:37:53.233TradeProcessor(EURUSD,H1)Ticketofthedeal:15258202NR017:37:53.233TradeProcessor(EURUSD,H1)Ticketoftheorder:22535869EF017:37:53.233TradeProcessor(EURUSD,H1)Volumeofthedeal:0.11MN017:37:53.233TradeProcessor(EURUSD,H1)Priceofthedeal:1.3137HJ017:37:53.233TradeProcessor(EURUSD,H1)Bid:1.3135PM017:37:53.233TradeProcessor(EURUSD,H1)Ask:1.3137OG017:37:53.233TradeProcessor(EURUSD,H1)Commenttotheoperation:RQ017:37:53.233TradeProcessor(EURUSD,H1)RequestID:1
通过分析处理器的其他参数,例如请求和响应的结构,您可以获得关于第一个请求调用的附加信息。
注意,第二个调用是将订单添加到打开的订单列表中(图。2)。
传说。2。类型字段等于交易记录订单添加
“transaction”块是日志中唯一需要的东西。
在订单中,我们可以看到订单号和其他参数(名称、价格和数量)已收到并包含在已完成订单列表中。
对事件处理程序的第三个调用是从已经打开的订单列表中删除订单(图)。3)。
传说。三。类型字段等于交易记录订单删除
除交易类型外,此处没有新信息。
对处理器的第四个调用是当新的历史订单出现在历史数据中时。(传说)4)。
传说。4。类型字段等于交易记录添加
在这个阶段,我们可以看到订单已经执行。
最后,第五个调用在事务订单添加到历史记录(图例)时发生。5)。
传说。5。类型字段等于交易记录交易记录添加
在日志中,我们只对程序块中的“事务”感兴趣。
在这个块中,最重要的字符串是事务号。
我要提出一个商业计划。对于职位,他们只有两部分。第一部分看起来像传说。6。
图6。第一交易流计划
仓库处理的所有交易操作均按照本计划进行。这里唯一的例外是修改位置操作。最终操作包括以下事务流(图例)。7)。
传说。7。二次交易流程规划
因此,在交易和订单历史记录中无法跟踪对头寸的修改。
几乎有所有关于职位的信息。
对于限价订单,应注意,它们的交易量较少。同时,在订单处理中,交易类型的组合也更多。
为了修改订单,处理器被调用两次,类似于修改仓库。下单和删除需要调用三次。删除或执行订单时,将调用四次TradeTransaction事件。
现在,我们将下限价订单。我们需要在任何图表的MetaTrader5终端上以调试模式启动EA。
对处理器的第一个调用是请求连接(图8)。
传说。8。类型字段等于交易请求
日志中将包含以下条目:
处理器的第二次调用将把订单添加到打开的订单列表中(图)。9)。
传说。9。类型字段等于添加的交易记录订单
在日志中,我们只需要查看事务块。
特别是,订单状态接收订单状态的值。
传说。10。类型字段等于交易记录订单更新
在日志中,我们只需要查看事务块中的记录。
这里最重要的字符串是订单的状态。
与仓库处理不同,限价订单处理不能通过计划来实现。限价订单的每个操作在交易类型方面都是唯一的。
下限订单将生成三个交易(图例。11)。
传说。11。下限订单交易流
修改限制订单将导致两个交易(图12)。
传说。12。修改有限订单交易流程
如果删除限制顺序,将调用四次OnTradeTransaction()处理器(图例)。13)。
传说。13。有限订单删除交易流程
删除限额订单由以下计划(图例)定义。14)。
传说。14。有限订单删除交易流程
触发限价订单,最终交易操作将导致四个不同的交易(图例)。15)。
传说。15。有限订单激活交易流
我不打算在这里为每个事务组合输入日志条目。如果读者愿意这样做,他们可以通过执行代码来判断。
让我们通过最终用户的眼睛来了解处理TradeTransaction事件的程序。最终用户可能需要一个与订单和仓库完美配合的程序。程序员必须对onstradeTransaction()进行编码,以便它能够识别所有事务及其组合—仓库或订单,而不管流程如何。理想情况下,程序可以指示可以执行哪些操作来完成一系列事务处理。
因此,如果要求编写一个接近理想工作的程序,您可以改进建议的样本,使事务处理独立于事务的到达顺序。
一般来说,仓库和订单可以有共同的交易类型。有11种交易类型。其中只有四个需要在来自终端的事务中处理:
我们不打算在本文中讨论它们。根据开发人员的不同,可以将这些类型设计为扩展事务服务器的功能。我必须承认,我以前没有处理过这些类型的问题。
这就为我们提供了七种功能齐全的类型,以便从OnTradeTransaction()中获取最常用的流程。
在处理器主体的这一部分中,定义了当前事务类型,并发挥了极其重要的作用。
//---==========Typesoftransaction[START]switch(trans_type){//---1)ifitisarequestcaseTRADE_TRANSACTION_REQUEST:{//---break;}//---2)ifitisanadditionofanewopenordercaseTRADE_TRANSACTION_ORDER_ADD:{//---break;}//---3)ifitisadeletionofanorderfromthelistofopenonescaseTRADE_TRANSACTION_ORDER_DELETE:{//---break;}//---4)ifitisanadditionofanewordertothehistorycaseTRADE_TRANSACTION_HISTORY_ADD:{//---break;}//---5)ifitisanadditionofadealtohistorycaseTRADE_TRANSACTION_DEAL_ADD:{//---break;}//---6)ifitisamodificationofapositioncaseTRADE_TRANSACTION_POSITION:{//---break;}//---7)ifitisamodificationofanopenordercaseTRADE_TRANSACTION_ORDER_UPDATE:{//---break;}}//---==========Typesoftransactions[END]我们将尝试定义当前事务类型可以处理哪些事务操作。为了找出我们正在做什么-职位或订单,我们将事务操作类型分配给案例模块以处理请求。
模块如下所示:
//---1)ifitisarequestcaseTRADE_TRANSACTION_REQUEST:{//---last_action=request.action;stringaction_str;//---whatistherequestforswitch(last_action){//---а)onmarketcaseTRADE_ACTION_DEAL:{action_str="placeamarketorder";trade_obj=TRADE_OBJ_POSITION;break;}//---б)placeapendingordercaseTRADE_ACTION_PENDING:{action_str="placeapendingorder";trade_obj=TRADE_OBJ_ORDER;break;}//---в)modifypositioncaseTRADE_ACTION_SLTP:{trade_obj=TRADE_OBJ_POSITION;//---StringConcatenate(action_str,request.symbol,":modifythelevelsofStopLoss","andTakeProfit");//---break;}//---г)modifyordercaseTRADE_ACTION_MODIFY:{action_str="modifyparametersofthependingorder";trade_obj=TRADE_OBJ_ORDER;break;}//---д)deleteordercaseTRADE_ACTION_REMOVE:{action_str="deletependingorder";trade_obj=TRADE_OBJ_ORDER;break;}}//---if(InpIsLogging)Print("Requestreceived:"+action_str);//---break;}
在这种情况下,改变一些变量并不困难。
staticENUM_TRADE_REQUEST_ACTIONSlast_action;//marketoperationatthefirstpass最后一个动作变量将记住事件处理程序启动的原因。
staticENUM_TRADE_OBJtrade_obj;//specifiesthetradeobjectatthefirstpass
变量trade_obj保存正在处理的内容——位置或订单。要做到这一点,我们应该创建enum_trade_obj枚举。
此后,我们将继续处理贸易交易订单添加交易类型模块:
//---2)ifitisanadditionofanewopenordercaseTRADE_TRANSACTION_ORDER_ADD:{if(InpIsLogging){if(trade_obj==TRADE_OBJ_POSITION)Print("Openanewmarketorder:"+EnumToString(trans.order_type));//---elseif(trade_obj==TRADE_OBJ_ORDER)Print("Placeanewpendingorder:"+EnumToString(trans.order_type));}//---break;}
这个模块非常简单。因为仓库是在第一步处理的,所以在当前位置会出现“打开一个新的市场订单”的日志条目,否则会出现“放置一个新的待处理订单”。在此块中,没有其他操作信息。
现在它是第三个处理交易订单删除类型的模块:
//---3)ifitisadeletionofanorderfromthelistofopenonescaseTRADE_TRANSACTION_ORDER_DELETE:{if(InpIsLogging)PrintFormat("Orderdeletedfromthelistofopenones:#%d,"+EnumToString(trans.order_type),trans.order);//---break;}
该模块也只有一个角色。
第四个案例模块处理交易记录添加类型:
模块检查第三遍的字符串,我们需要再次参考事务历史记录。如果没有发现交易,我们相信订单已经取消。
第五个案例模块处理交易交易添加类型。根据字符串的大小,这是程序的最大模块。
查看此块中的交易。根据单个数字选择事务以获得其属性是很重要的。如果职位是空缺或持平,则可以提供交易类型。在这里也可以得到极限阶的触发信息。在一种情况下,当TradeTransaction事件处理程序工作时,交易价格受限的订单。
交易类型trade_transaction_position是唯一的,仅在仓库修改时处理:
//---6)ifitisamodificationofapositioncaseTRADE_TRANSACTION_POSITION:{is_to_reset_cnt=true;//---PrintFormat("Modificationofaposition:%s",deal_symbol);//---if(InpIsLogging){PrintFormat("Newpriceofstoploss:%0."+IntegerToString(_Digits)+"f",trans.price_sl);PrintFormat("Newpriceoftakeprofit:%0."+IntegerToString(_Digits)+"f",trans.price_tp);}//---break;}
最终案例模块允许处理交易订单更新类型。
//---7)ifitisamodificationofanopenordercaseTRADE_TRANSACTION_ORDER_UPDATE:{//---ifitwasthefirstpassif(gTransCnt==0){trade_obj=TRADE_OBJ_ORDER;PrintFormat("Cancelingtheorder:#%d",trans.order);}//---ifitwasthesecondpassif(gTransCnt==1){//---ifitisanordermodificationif(last_action==TRADE_ACTION_MODIFY){PrintFormat("Pendingordermodified:#%d",trans.order);//---clearcounteris_to_reset_cnt=true;}//---ifitisdeletionoftheorderif(last_action==TRADE_ACTION_REMOVE){PrintFormat("Deletependingorder:#%d",trans.order);}}//---ifitwasthethirdpassif(gTransCnt==2){PrintFormat("Anewpendingorderwasplaced:#%d,"+EnumToString(trans.order_type),trans.order);//---clearcounteris_to_reset_cnt=true;}//---break;}总之,如果此类型在首次触发OnTradeTransaction()时发生,则可以取消或执行订单。
如果此类型发生在事件处理程序的第二个开头,则可以删除或修改顺序。要查找订单的确切结果,请参阅包含最后一个事务操作的静态变量last_action。
第三个引导事件处理程序是这种类型的最后一个案例。第三次开始完成下限订单的过程。
布尔变量“重置”也用于代码中。它用作清除OnTradeTransaction()处理器传输计数的标志。
这几乎与TradeTransaction事件过程有关。我还在对处理器的调用开始时添加了一个停顿。在转移到历史数据之前,它为关闭交易或订单提供了一个稍微延迟的机会。
在本文中,我试图描述可以使用哪些不同的事务操作,以及如何检索有关终端中发生的事件的信息。
这种方法的最大优点是程序可以接收有关事务操作的阶段性实现的信息。在我看来,这种方法可以用于将事务从一个终端复制到另一个终端。