外汇EA编写教程:在MetaTrader 5中实现多货币模型

简介

目前,市场上有大量成熟的多货币交易系统、指标和“EA交易”。然而,开发商在开发多货币系统时仍面临着一些具体问题。

随着MetaTrader 5客户端和MQL5编程语言的发布,我们获得了实现完整的多货币模型以及更高效的多货币机器人和指示器的新机会。这些新的机会是本文的主题。

传统方法概述

对于我们来说,传统的方法是尝试实现一个基于标准ontick()和oncalculate()函数的多货币系统(替换mql4中的start()函数)。简单地说,当新的价格变化或列出现在当前图表上时,所有(多货币系统)对货币都按顺序请求进行后续分析和决策。

该方法存在以下问题:

  1. 整个系统取决于当前图表交易品种的到达价格变化。
    是一个快速市场。当前图表中交易品种的价格变化非常频繁,不会有真正的问题。例如,对于缓慢的市场,在夜间,价格变动可能很少见:每半分钟变动一次或更少。在到达价格变化的稀疏区间内,即使其他交易品种变化很频繁,整个多货币系统仍处于“休眠”状态。如果系统被配置为按更大的计划工作,那么这个缺点就不那么明显了。时间表越小,影响越明显。世界每天都在加速,计算机每单位时间可以处理越来越多的信息,因此越来越多的人倾向于使用较小的周期甚至价格变化。
  2. 跨多货币系统中使用的所有交易类型同步历史数据的复杂性。”
    “在MetaTrader 4中,只会绘制价格至少变化一次的列。如果一分钟内没有价格变化,图表上会出现一分钟的单列空白。-“无间隙”图表从这个开始。

    是一种图形化方法,保留在MetaTrader 5中。换句话说,在图表上拥有相同数量的列并不意味着它们是按时间同步的。例如,对于每个交易项目,第100列可能有不同的打开时间。因此,在构建和重新计算多货币指标的过程中,必须确保每个交易品种的所有列都是一致的。

    ,但如果系统配置为按更大的时间表工作,这也不会很明显,因为随着周期的延长,列丢失的可能性急剧下降。不过,俗话说,你不能太小心。但除非你什么都不做,否则你永远无法预见。

    应注意不完整列的同步时间。当一个新列出现在当前图表上时,这并不意味着在其他交易品种中也有新列。因此,尝试使用copyXXXX()函数通过其他交易品种查询新列的价格可能会导致以下情况:您将获得该交易品种上某列的价格,或者只是复制错误。顺便说一下,其他贸易品种的新支柱可能比现有贸易品种的新支柱形成得早得多。这也可能影响态势评估的准确性。

    “使用多个中间指标缓冲区创建多货币指标”中引入的选项或多或少可以解决历史数据同步问题。

  3. 另一个关键点与数据同步有关:我们如何发现一些贸易产品更新了历史数据?例如,当我们构建一个单一的货币指标时,就没有问题了。如果输入变量prev_calculated of function oncalculate()返回零,则重新计算索引。但是,如果交易品种的历史数据是更新的,而不是在当前图表上,我们应该怎么做呢?这可能随时发生,可能需要重新计算多货币指标。这个问题的答案至关重要。

即使不看其他方面,我们也可以看到这三个例子足以引起许多问题:多货币EA或指标的代码太长。这个问题根本没有解决。

Ontimer()函数的新希望

mql程序生成计时器事件和ontimer()标准事件处理程序的新功能带来了一种新型多货币系统的出现。这是因为,一方面,多货币“ea trading”/指标现在可以通过当前图表独立于各种交易来接收价格变化;另一方面,我们可以实时监控ea的工作。但是…

该决议解决了前一节第1段中描述的问题,当然也带来了一些优势。但是,正如通过接收计时器事件接收newtick事件一样,要跟踪更改,必须按顺序查询所有货币对。有时这种情况经常发生,这显著提高了MQL5程序的资源使用率。

第2款和第3款中提出的问题仍然处于相同的解决水平。此外,必须解决ontimer()函数的独特问题。例如,计时器初始化/取消初始化问题,它在周末工作,等等。

我不打算轻视ontimer()事件处理程序的明显优势,但必须注意的是,它仍然无法实现完整的多货币模型。

onchartevent()函数带来的新可能性

这些标准函数的局限性源于其狭窄的具体化:它们被设计用来处理特定的、预先定义的事件,而不是用于多货币系统。

onchartevent()标准自定义事件处理程序对于多货币系统开发人员来说是“救命稻草”。它允许程序员根据自己的决策生成自己的事件。

例如,没有人可以阻止我们使用此函数来获取当前图表上任何项目的价格变化。我们所要做的就是发送一个“间谍”到一个具有相应种类事务的图表。

使用“间谍”这个笑话术语,我指的是以下指标:

//+------------------------------------------------------------------+
//|                                                         iSpy.mq5 |
//|                                            Copyright 2010, Lizar |
//|                                               lizar-2010@mail.ru |
//+------------------------------------------------------------------+
#define VERSION         "1.00 Build 2 (26 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/ru/users/Lizar"
#property version     VERSION
#property description "iSpy agent-indicator. If you want to get ticks, attach it to the chart"
#property indicator_chart_window

input long            chart_id=0;        // chart id
input ushort          custom_event_id=0; // event id

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,        // size of price[] array
                 const int prev_calculated,  // bars, calculated at the previous call
                 const int begin,            // starting index of data
                 const double& price[]       // array for the calculation
   )
  {
   double price_current=price[rates_total-1];

   //--- Initialization:
   if(prev_calculated==0) 
     { // Generate and send "Initialization" event
      EventChartCustom(chart_id,0,(long)_Period,price_current,_Symbol);
      return(rates_total);
     }
   
   // When the new tick, let's generate the "New tick" custom event
   // that can be processed by Expert Advisor or indicator
   EventChartCustom(chart_id,custom_event_id+1,(long)_Period,price_current,_Symbol);
   
   //--- return value of prev_calculated for next call
   return(rates_total);
  }

我们可以将“spy”放入所需事务的图表中,然后使用onchartevent()函数来处理EA或度量中的消息。要正确解码“间谍”消息,必须按以下方式解释参数:

  • ID-事件标识符。如果id-chartevent_custom=0,则我们的spy报告变量prev_calculated已返回零,应进行相应操作。
  • lparam——在这种情况下,它表示“间谍”开始的图表周期;
  • dparam-可变价格。默认情况下,这是最终收盘价。尽管可以将其设置为Spy启动期间枚举枚举应用的任何值;
  • sparam-事件接收到的事务种类的名称。

为了演示多个“spies”的同步,我们将编写一个简单的ea exspy.mq5(完整版本见文件):

//+------------------------------------------------------------------+
//|                                                        exSpy.mq5 |
//|                                            Copyright 2010, Lizar |
//|                            https://www.mql5.com/ru/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION       "1.00 Build 1 (26 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/ru/users/Lizar"
#property version     VERSION
#property description "The Expert Advisor shows the work of iSPY indicator"

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {   
   if(iCustom("GBPUSD",PERIOD_M1,"iSpy",ChartID(),0)==INVALID_HANDLE) 
      { Print("Error in setting of spy on GBPUSD"); return(true);}
   if(iCustom("EURUSD",PERIOD_M1,"iSpy",ChartID(),1)==INVALID_HANDLE) 
      { Print("Error in setting of spy on EURUSD"); return(true);}
   if(iCustom("USDJPY",PERIOD_M1,"iSpy",ChartID(),2)==INVALID_HANDLE) 
      { Print("Error in setting of spy on USDJPY"); return(true);}
      
   Print("Spys ok, waiting for events...");
   //---
   return(0);
  }
  
//+------------------------------------------------------------------+
//| The Standard event handler.                                      |
//| See MQL5 Reference for the details.                              |
//|                                                                  |
//| In this case it is used for decoding of spy messages, sent by    |
//| iSPY spy indicator                                               |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event id:
                                     // if id-CHARTEVENT_CUSTOM=0-"initialization" event
                const long&   lparam, // chart period
                const double& dparam, // price
                const string& sparam  // symbol
               )
  {
   if(id>=CHARTEVENT_CUSTOM)      
     {
      Print(TimeToString(TimeCurrent(),TIME_SECONDS)," -> id=",
            id-CHARTEVENT_CUSTOM,":  ",sparam," ",
            EnumToString((ENUM_TIMEFRAMES)lparam)," price=",dparam);
     }
  }
//+------------------------------------------------------------------+

在任何图表上开始“EA交易”。

结果如下:


从日志中,我们可以看到所需交易品种的所有价格变化和“初始化”事件,特别是在更新或上传历史数据时。

让我们总结一下中间结果:

  • 正如我们在使用标准ontick()和oncalculate()函数时所做的那样,我们不依赖于特定交易品种的价格变化。
  • 当使用ontimer()时,我们不局限于严格定义的时间间隔。
  • 没有必要要求所有的交易品种都按顺序或周期来跟踪它们的变化。一旦交易品种发生变化,我们就得到相应的事件。我们可以使用ID事件标识符或SPARAM参数来标识事件发生的事务类型。
  • 我们解决了每个事务的历史数据与服务器同步的问题。
  • 所给工作的一小部分分配给其他程序。这与数据处理的并行化有关。
  • 在onchartevent()函数的参数中,我们还可以得到其他信息:交易品种的周期、价格和名称。这可以大大简化EA或度量标准的代码,因为这些数据不需要额外的查询。

到目前为止,由于我们已经通过MetaTrader 5终端和MQL5编程语言成功地实现了多货币模型,我们基本上能够满意地结束本文。但这只是一个“原始”的实现。因此,我们将进一步讨论这个问题。

多货币模型的实现

如果你使用上述的想法来实现一个纯形式的多货币系统,你会在某种意义上遇到麻烦。问题是,这种方法允许我们获得“间谍”进行的每一笔交易的所有价格变化。

对于快速的市场,每一个交易项目在一秒钟内可能会有很多价格变化。这可能导致事件序列的“阻塞”。以下是“帮助”部分的警告消息:

客户机添加出现在事件队列中的事件。因此,事件按接收顺序逐个处理。newtick事件是一个例外。如果队列中已存在或正在处理此类事件,则新的newtick事件将不会进入队列。

事件队列的大小受到限制。如果队列溢出,将直接删除旧事件,而不进行处理,以允许接受新事件。因此,建议编写高效的事件处理程序,而不要使用无限循环(使用只处理开始事件的异常脚本)。

队列的溢出会导致多货币指标的丢失或EA的重要事件。这是一个方面。另一方面,我们并不总是需要对所有类型的交易进行价格变动。有时我们只需要在任何时间表上获得“新支柱”活动。或者在不同的时间表上出现多个“新支柱”事件。从根本上讲,我们的“间谍”不适合这样的要求,使用起来也不太方便。

让我们使其具有普遍性,这样我们就不会回到如何基于多货币EA或指数交易品种获取事件的问题上。为此,我们以多货币“ea trading”和指标的“mcm control panel”中的枚举u图表u事件u符号事件为例:

enum ENUM_CHART_EVENT_SYMBOL
  {
   CHARTEVENT_INIT      =0,         // "Initialization" event
   CHARTEVENT_NO        =0,         // No events

   CHARTEVENT_NEWBAR_M1 =0x00000001, // "New bar" event on M1 chart
   CHARTEVENT_NEWBAR_M2 =0x00000002, // "New bar" event on M2 chart
   CHARTEVENT_NEWBAR_M3 =0x00000004, // "New bar" event on M3 chart
   CHARTEVENT_NEWBAR_M4 =0x00000008, // "New bar" event on M4 chart
   
   CHARTEVENT_NEWBAR_M5 =0x00000010, // "New bar" event on M5 chart
   CHARTEVENT_NEWBAR_M6 =0x00000020, // "New bar" event on M6 chart
   CHARTEVENT_NEWBAR_M10=0x00000040, // "New bar" event on M10 chart
   CHARTEVENT_NEWBAR_M12=0x00000080, // "New bar" event on M12 chart
   
   CHARTEVENT_NEWBAR_M15=0x00000100, // "New bar" event on M15 chart
   CHARTEVENT_NEWBAR_M20=0x00000200, // "New bar" event on M20 chart
   CHARTEVENT_NEWBAR_M30=0x00000400, // "New bar" event on M30 chart
   CHARTEVENT_NEWBAR_H1 =0x00000800, // "New bar" event on H1 chart
   
   CHARTEVENT_NEWBAR_H2 =0x00001000, // "New bar" event on H2 chart
   CHARTEVENT_NEWBAR_H3 =0x00002000, // "New bar" event on H3 chart
   CHARTEVENT_NEWBAR_H4 =0x00004000, // "New bar" event on H4 chart
   CHARTEVENT_NEWBAR_H6 =0x00008000, // "New bar" event on H6 chart
   
   CHARTEVENT_NEWBAR_H8 =0x00010000, // "New bar" event on H8 chart
   CHARTEVENT_NEWBAR_H12=0x00020000, // "New bar" event on H12 chart
   CHARTEVENT_NEWBAR_D1 =0x00040000, // "New bar" event on D1 chart
   CHARTEVENT_NEWBAR_W1 =0x00080000, // "New bar" event on W1 chart
     
   CHARTEVENT_NEWBAR_MN1=0x00100000, // "New bar" event on MN1 chart
   CHARTEVENT_TICK      =0x00200000, // "New tick" event
   
   CHARTEVENT_ALL       =0xFFFFFFFF, // All events
  };

实际上,此枚举是自定义图表事件的标志。它是一个最小集合,可能是多货币模型所需要的。当然,我们可以补充一下。标识的组合将决定我们要从“间谍”那里发出的事件。

可以使用逐位操作“或”组合标志。例如,chartevent_newbar_m1_chartevent_newbar_h1的组合表明,我们将在“间谍”的帮助下,从分钟和小时计划中发布“新支柱”。这些标志将是我们的“间谍”索引的输入参数。接下来我们称之为“代理指示器”。

指标本身符合新概念。代码如下:

//+------------------------------------------------------------------+
//|                                        Spy Control panel MCM.mq5 |
//|                                            Copyright 2010, Lizar |
//|                            https://www.mql5.com/zh/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION       "1.00 Build 3 (26 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/zh/users/Lizar"
#property version     VERSION
#property description "This is the MCM Control Panel agent-indicator."
#property description "Is launched on the required symbol on any time-frame"
#property description "and generates the custom NewBar event and/or NewTick"
#property description "for the chart which receives the event"

#property indicator_chart_window
  
input long                    chart_id;                 // identifier of the chart which receives the event
input ushort                  custom_event_id;          // event identifier  
input ENUM_CHART_EVENT_SYMBOL flag_event=CHARTEVENT_NO;// indicator, which determines the event type.

MqlDateTime time, prev_time;

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,       // size of the price[] array
                 const int prev_calculated, // bars processed at the previous call
                 const int begin,           // where the data begins
                 const double& price[]      // calculations array
   )
  {  
   double price_current=price[rates_total-1];

   TimeCurrent(time);
   
   if(prev_calculated==0)
     {
      EventCustom(CHARTEVENT_INIT,price_current);
      prev_time=time; 
      return(rates_total);
     }
   
//--- new tick
   if((flag_event & CHARTEVENT_TICK)!=0) EventCustom(CHARTEVENT_TICK,price_current);       

//--- check change time
   if(time.min==prev_time.min && 
      time.hour==prev_time.hour && 
      time.day==prev_time.day &&
      time.mon==prev_time.mon) return(rates_total);

//--- new minute
   if((flag_event & CHARTEVENT_NEWBAR_M1)!=0) EventCustom(CHARTEVENT_NEWBAR_M1,price_current);     
   if(time.min%2 ==0 && (flag_event & CHARTEVENT_NEWBAR_M2)!=0)  EventCustom(CHARTEVENT_NEWBAR_M2,price_current);
   if(time.min%3 ==0 && (flag_event & CHARTEVENT_NEWBAR_M3)!=0)  EventCustom(CHARTEVENT_NEWBAR_M3,price_current); 
   if(time.min%4 ==0 && (flag_event & CHARTEVENT_NEWBAR_M4)!=0)  EventCustom(CHARTEVENT_NEWBAR_M4,price_current);      
   if(time.min%5 ==0 && (flag_event & CHARTEVENT_NEWBAR_M5)!=0)  EventCustom(CHARTEVENT_NEWBAR_M5,price_current);     
   if(time.min%6 ==0 && (flag_event & CHARTEVENT_NEWBAR_M6)!=0)  EventCustom(CHARTEVENT_NEWBAR_M6,price_current);     
   if(time.min%10==0 && (flag_event & CHARTEVENT_NEWBAR_M10)!=0) EventCustom(CHARTEVENT_NEWBAR_M10,price_current);      
   if(time.min%12==0 && (flag_event & CHARTEVENT_NEWBAR_M12)!=0) EventCustom(CHARTEVENT_NEWBAR_M12,price_current);      
   if(time.min%15==0 && (flag_event & CHARTEVENT_NEWBAR_M15)!=0) EventCustom(CHARTEVENT_NEWBAR_M15,price_current);      
   if(time.min%20==0 && (flag_event & CHARTEVENT_NEWBAR_M20)!=0) EventCustom(CHARTEVENT_NEWBAR_M20,price_current);      
   if(time.min%30==0 && (flag_event & CHARTEVENT_NEWBAR_M30)!=0) EventCustom(CHARTEVENT_NEWBAR_M30,price_current);      
   if(time.min!=0) {prev_time=time; return(rates_total);}
//--- new hour
   if((flag_event & CHARTEVENT_NEWBAR_H1)!=0) EventCustom(CHARTEVENT_NEWBAR_H1,price_current);
   if(time.hour%2 ==0 && (flag_event & CHARTEVENT_NEWBAR_H2)!=0)  EventCustom(CHARTEVENT_NEWBAR_H2,price_current);
   if(time.hour%3 ==0 && (flag_event & CHARTEVENT_NEWBAR_H3)!=0)  EventCustom(CHARTEVENT_NEWBAR_H3,price_current);      
   if(time.hour%4 ==0 && (flag_event & CHARTEVENT_NEWBAR_H4)!=0)  EventCustom(CHARTEVENT_NEWBAR_H4,price_current);      
   if(time.hour%6 ==0 && (flag_event & CHARTEVENT_NEWBAR_H6)!=0)  EventCustom(CHARTEVENT_NEWBAR_H6,price_current);      
   if(time.hour%8 ==0 && (flag_event & CHARTEVENT_NEWBAR_H8)!=0)  EventCustom(CHARTEVENT_NEWBAR_H8,price_current);      
   if(time.hour%12==0 && (flag_event & CHARTEVENT_NEWBAR_H12)!=0) EventCustom(CHARTEVENT_NEWBAR_H12,price_current);      
   if(time.hour!=0) {prev_time=time; return(rates_total);}
//--- new day
   if((flag_event & CHARTEVENT_NEWBAR_D1)!=0) EventCustom(CHARTEVENT_NEWBAR_D1,price_current);      
//--- new week
   if(time.day_of_week==1 && (flag_event & CHARTEVENT_NEWBAR_W1)!=0) EventCustom(CHARTEVENT_NEWBAR_W1,price_current);      
//--- new month
   if(time.day==1 && (flag_event & CHARTEVENT_NEWBAR_MN1)!=0) EventCustom(CHARTEVENT_NEWBAR_MN1,price_current);      
   prev_time=time;
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

void EventCustom(ENUM_CHART_EVENT_SYMBOL event,double price)
  {
   EventChartCustom(chart_id,custom_event_id,(long)event,price,_Symbol);
   return;
  } 

此指示器是MCM控制面板的一部分,我们没有重命名它。附件仅包含其更新版本(请参阅“Spy控制面板MCM”。MQ5“)。但这并不意味着它不能与面板分开使用。

代理度量使用onchartevent()函数生成自定义用户事件,并将其传递给图表接收器,以便在EA或度量中进一步处理这些事件。现在,函数的输入参数应该解释如下:

  • ID事件标识符;
  • lparam—事件指示器,从面板代理接受。这些指示器对应于枚举图表事件符号枚举。
  • dparam——特定时间表中新列的可变价格或开盘价;
  • sparam-事件发生的事务类型的名称。

 demo ea看起来并不比以前的ea(附加到文件的完整版本)复杂:

//+------------------------------------------------------------------+
//|                                      exSpy Control panel MCM.mq5 |
//|                                            Copyright 2010, Lizar |
//|                            https://www.mql5.com/zh/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION       "1.00 Build 1 (28 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/zh/users/Lizar"
#property version     VERSION
#property description "The EA demonstrates the work of the MCM Spy Control panel"

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {   
   if(iCustom("GBPUSD",PERIOD_M1,"Spy Control panel MCM",ChartID(),0,
             CHARTEVENT_NEWBAR_M1|CHARTEVENT_NEWBAR_M5)==INVALID_HANDLE) 
      { Print("Error in setting of spy on GBPUSD"); return(true);}
   if(iCustom("EURUSD",PERIOD_M1,"Spy Control panel MCM",ChartID(),1,
             CHARTEVENT_NEWBAR_M2)==INVALID_HANDLE) 
      { Print("Error in setting of spy on EURUSD"); return(true);}
   if(iCustom("USDJPY",PERIOD_M1,"Spy Control panel MCM",ChartID(),2,
             CHARTEVENT_NEWBAR_M6)==INVALID_HANDLE) 
      { Print("Error in setting of spy on USDJPY"); return(true);}
      
   Print("Spys ok, waiting for events...");
   //---
   return(0);
  }
  
//+------------------------------------------------------------------+
//| The Standard event handler.                                      |
//| See MQL5 Reference for the details.                              |
//|                                                                  |
//| In this case it is used for decoding of spy messages, sent by    |
//| iSPY Control panel MCM indicator.                                |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,           // event identifier
                  const long&   lparam, // the event flag.
                                       // event flag is a bit mask of ENUM_CHART_EVENT_SYMBOL enumeration.
                  const double& dparam, // price
                  const string& sparam  // symbol 
                 )
  {
   if(id>=CHARTEVENT_CUSTOM)      
     {
      Print(TimeToString(TimeCurrent(),TIME_SECONDS)," -> id=",id-CHARTEVENT_CUSTOM,
           ":  ",sparam," ",EnumToString((ENUM_CHART_EVENT_SYMBOL)lparam)," price=",dparam);
     }
  }

ExSpy控制面板MCM的工作结果:


如您所见,我们定期收到所有活动请求。

再次,让我们简单总结一下中间结果:

  • 由于新版本的代理指标,我们保留了所有以前的成就。
  • 现在,我们可以通过多货币MQL程序指定至少23个事件,包括“新价格更改”事件、“新列”事件和“初始化”事件。
  • 将EA/Metrics中的更多工作分配给代理进行卸载。

事实证明,在元交易员5中实现一个完整的多货币模型并不那么困难。

另外,我想强调一些细微的差异。

首先,“代理”为我们的多货币EA/指标生成的所有事件都是外部事件。出现了一个相关的问题:“是否需要直接从EA/Metrics运行代理?”我的回答是“不”。

其次,在onchartevent()函数中,事件标识符id似乎是多余的,因为我们可以通过参数sparam(事务类型的名称)找到事件接收到的事务类型。所以也许我们可以把它用于其他目的?我的回答是:“是的,我们可以。”

这些观点导致了多货币“EA交易”和指标的“MCM控制面板”的出现。它是终端和EA/Indicators之间的“中间层”,为我们配置多货币环境提供了更多的好处和灵活性。

  • 面板可以作为单独的指示器安装在图表上,然后分配与面板兼容的多货币指示器。
  • 面板可以包括指示器和EA组件。它将与他们一起上传。nbsp;
  • 我们可以使用事件菜单从市场观察窗口激活/停用交易品种进行交易或分析。在onchartevent()函数中,我们无法通过事件标识符ID在Market Watch窗口中找到交易项目的序列号。
  • 我们可以通过市场观察窗口中选择的任何时段和任何交易品种的价格变化或“新支柱”事件来建立交易模式。所有这些都是通过常规菜单完成的。
  • 我们可以在不卸载、停止或进入EA或度量的属性窗口的情况下更改所有上述配置。
  • 所有这些都不会限制创建多货币指标和EAS的创新潜力。此外,我们现在不需要将面板的输出合并到代码中。管理机构指标已在控制面板中实施。
  • 多货币系统的结构更简单。

美元指数的多货币指数RSI

为了体验上述方法的所有优点,我建议使用MCM控制面板来实现USDX美元指数的RSI指数的多货币变体。

首先,我想指出几个特点。通常,在分析美元指数时,我们只依赖指数的指数读数。在我看来,这并不完全正确,因为货币对指数篮子中的每一笔交易都有自己的贡献。因此,作为一个例子,我们使用一个类似于指数公式的公式来计算美元指数的RSI:

换句话说,我们首先计算特定货币对的RSI,然后读取指数的RSI,同时考虑价格系数的权重。

读者可能会注意到与多货币系统中使用的所有类型交易的历史数据同步相关的问题。(见“传统方法概述”一节第2段)。

利用类函数构造rsi同步缓冲区(synchronized buffer rsi),解决了这个问题。MQH文件)。为这个类提供完整的代码是没有意义的,因此下面只列出了几个相关的部分。

首先,使用公共访问修饰符在类中定义指示器缓冲区:

public:
   double   buffer[];   // indicator buffer

其次,索引初始化使用类方法初始化:

//--- Initialization methods:
bool Init(int n,string symbol,int rsi_count,int rsi_period);

第三,对于每一列,使用类的刷新方法通过当前调度同步索引缓冲区的值:

//+------------------------------------------------------------------+
//| The method of receiving/updating indicator data for one bar      |
//| of the indicator buffer.                                         |
//| INPUT:  bar   - bar number                                       |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CSynchronizedBufferRSI::Refresh(int bar=0)
  {
   buffer[bar]=EMPTY_VALUE; // Initialization of the bar of the indicator buffer.
     
   //--- Inquire the time of the bar for the current graph:
   datetime time[1];      
   if(CopyTime(_Symbol,_Period,bar,1,time)!=1) return; // In case of an error, we wait for the next tick/bar...

   //--- Request the value of the indicator for the symbol for the time,
   //--- consistent with that of the bar of the current graph:
   double value[1];
   if(CopyBuffer(m_handle,0,time[0],time[0],value)!=1) return; // In case of an error, wait for the next tick/bar...

   buffer[bar]=value[0];
   return;
  }

为了完美地同步所有指示器缓冲区,我们需要使用后续文章中引入的整个“无间隙”分钟计划。但是对于索引缓冲区同步方法,我们选择当前图表的计划,因为索引显示在它上面。

根据我自己的经验,如果时间序列或指标缓冲区的交易品种不同于当前图表上的交易品种,则在较小的时期内使用这种同步方法是合理的。

图表清楚地显示了为什么值得这样做:


对于较大的时间表,通常不会观察到这一点。

最后,下面是度量中使用的标准用户事件处理程序的代码:

//+------------------------------------------------------------------+
//| The Standard event handler.                                      |
//| See MQL5 Reference for the details.                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event identifier or position symbol in the "Market Match"+CHARTEVENT_CUSTOM  
                const long& lparam,   // event indicator
                const double& dparam, // price
                const string& sparam  // symbol
                )
  {
   int custom_id=id-CHARTEVENT_CUSTOM-1;
   
   if(custom_id>=0)      
     {
      if(lparam!=CHARTEVENT_NEWBAR_NO)
        { 
         //--- Recalculation of the last uncompleted bar:
         if(EventToPeriod(lparam)==_Period && sparam==_Symbol)
           { // Recalculation of the indicator, if a new bar on the current chart
            iRSIUSDx_Ind[0]=EMPTY_VALUE;
            //--- Updating the value of the RSI for all of the currency pairs for the new bar
            for(int i=0;i<symbol_total;i++) buffer[i].Refresh();
            iRSIUSDx(symbol_total);   // calculation of the current incomplete bar RSI for the index
            return;
           }
         
         buffer[custom_id].Refresh(); // The value of RSI for the custom_id of the currency pair for the current bar
         iRSIUSDx(symbol_total);      // calculation of the RSI for the current(uncompleted) bar RSIx
         return;
        }
      else 
        { 
         //--- Recalculation of the indicator for the "Initialization" event 
         buffer[custom_id].RefreshBuffer();     // Update of the RSI buffer for the custom_id of the currency pair
         Init_iRSIUSDx(symbol_total,calculate); // Update of the RSI buffer for the index
         return;
        }
     }
  }

代码特征:

  • 事件标识符ID用于引用包含指向类实例的指针的指针数组,这些指针用于计算指示器缓冲区的RSI并与当前时间表同步。这种方法大大简化了代码的结构。
  • “初始化”事件仅用于为它接收的货币对重新计算索引缓冲区RSI,而不用于所有六种类型的事务。如前所述,这允许您同步度量,例如,在更新交易品种的历史数据时。
  • New Column事件用于同步当前图表上新列的所有指示器缓冲区的RSI。
  • 从所有货币对中,使用“新价格更改”事件更新最后一个未完成的列指标。此外,重新计算列仅适用于接收“新价格更改”的货币对。

在研究了美国农业部RSI指数的完整代码后,其工作模式更加清晰。

安装点:

  • 下载多货币“ea trading”和指标的“mcm control panel”,并编译“icontrol panel mcm.mq5”和“spy control panel mcm.mq5”文件。
  • 在市场匹配窗口中,需要指定以下交易品种的顺序:
    欧元兑美元、日元兑美元、英镑兑美元、加拿大元兑美元、瑞典克朗兑美元、瑞士法郎,因为我没有对指标进行适当的检查,正确计算指标的顺序是必要的。
  • 将irsiusdx.zip文件解压缩到/mql5文件夹。将irsiusdx.ex5从/mql5/indicators/irsiusdx/文件夹放在具有期间m1的欧元/美元图表上。
  • 如前所述,在控制面板MCM(控制面板MCM)面板的事件(事件)菜单中,依次为所有六个交易项目设置新的勾选(新价格变化)事件。你会得到一张和上面那张相似的照片。
  • 此外,对于欧元兑美元交易品种,在分钟图表上设置新的条形事件。在指示器中,当新列位于当前时间表(等于m1)上时,事件用于同步。
  • 如果您想要一个更直观的例子,请按照此处所述设置美元指数。

总结

元交易者5中完整的多货币模型的实现充分展示了平台和MQL5编程语言在解决这个问题上的优势。以前遇到的大多数困难现在都解决了。

显然,这只是朝着这个方向发展的开始。可以肯定的是,未来将有更多更好的数据同步和多货币管理方法。我们现在有了所有必要的工具,我希望这一点毫无疑问。

本文由MetaQuotes Software Corp.翻译自俄语原文
,网址为https://www.mql5.com/ru/articles/234。

附加文件下载zip irsiusdx_en.zip(7.54 kb)

 

 


MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经(www.myfxtop.cn)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投