English Русский Español Deutsch 日本語 Português
preview
开发回放系统(第30部分):EA交易项目——C_Mouse类(四)

开发回放系统(第30部分):EA交易项目——C_Mouse类(四)

MetaTrader 5测试者 | 9 五月 2024, 09:38
426 0
Daniel Jose
Daniel Jose

概述

在上一篇文章开发回放系统(第29部分):EA交易项目——C_Mouse类(III)中,我们设计C_Mouse类时,可以在不破坏代码任何部分的情况下扩展研究功能。由于我们依赖于允许并行创建代码的编程技术,因此我们将有一个以有组织的方式开发的主要项目。同时,如果需要,可以向系统添加额外的功能。这样,在实现这些函数时,我们的主代码就不会发生任何变化,也不会因为过度使用继承而卡顿。

虽然面向对象编程(OOP)是一种很好的编程方式,但它更适合于生产项目,在这些项目中,我们希望严格控制发生的事情,并避免随着系统的发展出现奇怪的错误。有时我们需要并行开发项目的一部分。虽然这听起来很奇怪,但当我们在程序中添加一些函数或方法时,将函数放在代码中间是不太合适的,这是它的早期阶段(在测试阶段),已经处于高级阶段。我的意思是,您不应该在已经测试和工作的代码中添加未经测试的函数,因为这可能会导致不可预测的失败。

由于这些缺陷,您通常不得不将整个项目重新投入到开发的早期阶段。有时,新函数可能嵌入到代码中,这样删除它比将项目返回到早期阶段要困难得多。尽管许多人,尤其是新程序员,实际上并没有使用目录结构来返回到开发的早期阶段,但即使不使用这样的目录结构,我们也可以使用某种技术,使我们能够将系统返回到添加的资源实际上不属于已完成项目的一部分的位置。

通过这种方式,我们可以并行开发,而最终项目进展顺利。在上一篇文章中,我们已经了解了如何使用指针。现在,让我们更进一步,在基本模型的基础上生成一个更复杂的研究。如果研究或资源被证明适合最终项目,一旦它通过了更高级的测试阶段,并确认足够稳定和可靠,它就可以被纳入主类系统。因此,以前被认为是次要项目的内容成为最终项目的一部分,在类系统中继承和被继承。

为了证明这一点,我们将创建C_Mouse类的修改,但不使用继承和多态性。我们将得到一个完全不同的分析模型,不同于C_Mouse类中的原始系统。要做到这一点,我们将创建一个新的类,该类可能(也可能不)继承自我们在上一篇文章中看到的C_Studies类。是否继承C_Studys类与其说是个实际问题,不如说是个个人问题。事实上,无论如何,一个项目与另一个项目无关,因为它们可以并行工作。尽管如此,任何属于主系统的代码都将继承C_Mouse类,直到扩展该类的代码被认为足够稳定和有趣,我们可以在最终项目中使用它。

在继续编程之前,重要的是要知道系统可以以两种不同的方式进行。我们选择的道路取决于我们想做什么以及我们想走多远。由于我们有两条路径,并且它们之间的差异很小,所以让我们看看全部这两条路径。在所附的代码中,您将可以访问两个路径中的一个。但如果你愿意,你可以做出必要的改变,走一条不同的道路。

我在这里的想法是展示我们在平台内可以做什么,而不是我们应该如何做。


添加到 C_Terminal 类

我们为演示目的而编程的系统不需要或不需要对主代码进行任何添加,但出于实际原因,为了开始测试我们在开发代码时经常使用的对象的创建,我们将添加一些在交易品种图表上创建对象的通用代码。通过这种方式,我们可以从一开始就开始测试和改进它。这方面的代码早就开发出来了,我们已经考虑选择一个更合适的位置。在我们找到更好的位置之前,create函数将位于C_Terminal类中,如下所示:

inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor, const int zOrder = -1)
   {
      ObjectCreate(m_Infos.ID, szName, obj, 0, 0, 0);
      ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n");
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder);
   }

此函数将成为创建将显示在图表上的对象的通用功能。在某些情况下,我们将看到它出现在只声明了两个元素的代码中。这是因为元素是用默认值声明的,所以不需要在调用时声明它,除非它的值由于某种原因而不同。此外,我们将始终看到函数在其声明中使用两个元素进行调用。关键是OBJPROP_ZORDER对象的此属性用于解决我们在某些情况下会看到的问题。如果我们没有正确定义此属性,在任何模式下使用程序时,放置在交易品种图表上的对象都会出现严重问题:回放/模拟、在演示或实时帐户上进行交易。我们已经看到了主代码的变化,现在我们了解了如何以不同于使用原始代码的方式使用系统。让我们在单独的主题中查看更多详细信息。


第一种方法:使用继承

在第一条路径中,我们使用继承,不是来自C_Mouse类,而是来自C_Study类,我们在上一篇文章中已经讨论过了。我们的头文件C_StudyS2.mqh将包含以下代码:

//+------------------------------------------------------------------+
#include "C_StudyS1.mqh"
//+------------------------------------------------------------------+

// ... Local definitions ....

//+------------------------------------------------------------------+

// ... Local alias ...

//+------------------------------------------------------------------+
class C_StudyS2 : public C_StudyS1
{
   protected:
   private :

// ... Code and internal functions ...

//+------------------------------------------------------------------+
   public  :
//+------------------------------------------------------------------+
      C_StudyS2(C_Mouse *arg, color corP, color corN)
         :C_StudyS1(arg, corP, corN)
      {                               
// ... Internal code ....

      }
//+------------------------------------------------------------------+
virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
      {
         double v1, v2;
         int w, h;
         string sz1;
                                
         C_StudyS1::DispatchMessage(id, lparam, dparam, sparam);

// ... Internal code ...

      }
//+------------------------------------------------------------------+
};

在这里,我们看到上一篇文章中的类是根据继承原则使用的,我们继承了类并添加了属性。在许多情况下,这将是最好的选择,但并非总是如此。重要的是要知道这些路径是如何不同和互补的。因此,通过使用这种方法,我们将能够最大限度地利用它。请注意以上摘录中强调的所有要点。别担心,我们将解释如何使用它来创造有趣的东西。

合乎逻辑的是,由于我们使用的是继承,因此这种情况下的EA、指标或脚本代码将与我们不使用继承时的情况略有不同。为了理解这些差异,我们首先来看第一种情况的EA代码。完整的代码如下所示:

#property copyright "Daniel Jose"
#property description "Generic EA for use on Demo account, replay system/simulator and Real account."
#property description "This system has means of sending orders using the mouse and keyboard combination."
#property description "For more information see the article about the system."
#property version   "1.30"
#property icon "../../Images/Icons/Replay - EA.ico"
#property link "https://www.mql5.com/en/articles/11372"
//+------------------------------------------------------------------+
#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
#include <Market Replay\System EA\Auxiliar\Study\C_StudyS2.mqh>
//+------------------------------------------------------------------+
input group "Mouse";
input color     user00 = clrBlack;      //Price Line
input color     user01 = clrPaleGreen;  //Positive Study
input color     user02 = clrLightCoral; //Negative Study
//+------------------------------------------------------------------+
C_Mouse *mouse = NULL;
C_StudyS2 *extra = NULL;
//+------------------------------------------------------------------+
int OnInit()
{
   mouse = new C_Mouse(user00, user01, user02);
   extra = new C_StudyS2(mouse, user01, user02);
                
   MarketBookAdd((*mouse).GetInfoTerminal().szSymbol);
   OnBookEvent((*mouse).GetInfoTerminal().szSymbol);
   EventSetMillisecondTimer(500);

   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   MarketBookRelease((*mouse).GetInfoTerminal().szSymbol);
   EventKillTimer();
        
   delete extra;
   delete mouse;
}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnTimer()
{
   (*extra).Update();
}
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
   MqlBookInfo book[];
   
   if (mouse.GetInfoTerminal().szSymbol == def_SymbolReplay) ArrayResize(book, 1, 0); else
   {
      if (symbol != (*mouse).GetInfoTerminal().szSymbol) return;
      MarketBookGet((*mouse).GetInfoTerminal().szSymbol, book);
   }
   (*extra).Update(book);
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   (*mouse).DispatchMessage(id, lparam, dparam, sparam);
   (*extra).DispatchMessage(id, lparam, dparam, sparam);
        
   ChartRedraw();
}
//+------------------------------------------------------------------+

与上一篇文章中的代码唯一的区别是突出显示的部分。这是因为我们使用继承系统。正如我们已经知道的,当我们希望系统顺利开发,没有许多意外事件时,继承系统运行得非常好。但我们最终可能会遇到其他问题,使我们的生活更加困难。有时我们需要使用稍微不同的方法,这将在应用程序中提供。如果我们想将系统用作基于继承的模型,那没关系。只要记住做出已经注意到的改变,一切都会顺利进行。


第二种方法:使用指针

在第二条路径中,我们将看到类代码的详细解释。首先,让我们看看EA代码是什么样子的。在这个阶段,我们将在类代码中有很大的差异,实际上只有前面主题中显示的内容才能补充这些差异。以下是遵循第二条路径的EA的完整代码:

#property copyright "Daniel Jose"
#property description "Generic EA for use on Demo account, replay system/simulator and Real account."
#property description "This system has means of sending orders using the mouse and keyboard combination."
#property description "For more information see the article about the system."
#property version   "1.30"
#property icon "../../Images/Icons/Replay - EA.ico"
#property link "https://www.mql5.com/en/articles/11372"
//+------------------------------------------------------------------+
#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
#include <Market Replay\System EA\Auxiliar\Study\C_StudyS1.mqh>
#include <Market Replay\System EA\Auxiliar\Study\C_StudyS2.mqh>
//+------------------------------------------------------------------+
input group "Mouse";
input color     user00 = clrBlack;      //Price Line
input color     user01 = clrPaleGreen;  //Positive Study
input color     user02 = clrLightCoral; //Negative Study
//+------------------------------------------------------------------+
C_Mouse *mouse = NULL;
C_StudyS1 *extra1 = NULL;
C_StudyS2 *extra2 = NULL;
//+------------------------------------------------------------------+
int OnInit()
{
   mouse = new C_Mouse(user00, user01, user02);
   extra1 = new C_StudyS1(mouse, user01, user02);
   extra2 = new C_StudyS2(mouse, user01, user02);
                
   MarketBookAdd((*mouse).GetInfoTerminal().szSymbol);
   OnBookEvent((*mouse).GetInfoTerminal().szSymbol);
   EventSetMillisecondTimer(500);

   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   MarketBookRelease((*mouse).GetInfoTerminal().szSymbol);
   EventKillTimer();
        
   delete extra1;
   delete extra2;
   delete mouse;
}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnTimer()
{
   (*extra1).Update();
}
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
   MqlBookInfo book[];
   
   if ((*mouse).GetInfoTerminal().szSymbol == def_SymbolReplay) ArrayResize(book, 1, 0); else
   {
      if (symbol != (*mouse).GetInfoTerminal().szSymbol) return;
      MarketBookGet((*mouse).GetInfoTerminal().szSymbol, book);
   }
   (*extra1).Update(book);
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   (*mouse).DispatchMessage(id, lparam, dparam, sparam);
   (*extra1).DispatchMessage(id, lparam, dparam, sparam);
   (*extra2).DispatchMessage(id, lparam, dparam, sparam);
        
   ChartRedraw();
}
//+------------------------------------------------------------------+

在这里,我展示了我们在哪里使用主类系统,用黄色标记。我们在上一篇文章中展示的扩展类系统用绿色标记。将执行不同类型分析的系统,但可能是其他系统,显示为橙色。注意,由于我们没有使用继承,我们必须在EA中声明更多的代码。同时,这使我们能够发布更多的并行代码来测试最终版本中的内容。最好的是,如果这个并行代码开始显示任何故障或错误,我们可以将其从代码中删除,而不会有太多麻烦。然而,这里还有另一个问题:橙色和绿色代码都可能是多态的。这使我们能够测试正在并行开发的系统的更多方面。我们将把多态性的话题留待下一次讨论。如果我们现在谈论它,我们会使解释过于复杂,这样爱好者可能不会真正明白多态性使用背后的所有原因。

在这些解释之后,您可以继续学习类代码。请记住,第一条路径和第二条路径的代码几乎相同,当然,除了主题中关于第一条路径的要点。


让我们分析一下C_StudyS2类的代码

从某种意义上说,与分析系统相关的所有代码都非常相似,只有少数例外。然而,有许多事情使分析生成代码的分析和理解变得有趣。让我们仔细看看这个。请记住,这里提供的代码仅用于演示目的,它决不是一个完整的方法。以下是C_StudyS2.mqh的开始方式:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "..\C_Mouse.mqh"
#include "..\..\..\Service Graphics\Support\Interprocess.mqh"
//+------------------------------------------------------------------+
#define def_ExpansionPrefix "Expansion2_"
#define def_ExpansionBtn1 def_ExpansionPrefix + "B1"
#define def_ExpansionFibo def_ExpansionPrefix + "FB"
//+------------------------------------------------------------------+
#define def_InfoTerminal (*mouse).GetInfoTerminal()
#define def_InfoMousePos (*mouse).GetInfoMouse().Position
//+------------------------------------------------------------------+
class C_StudyS2
{
   protected:
   private :
//+------------------------------------------------------------------+
      C_Mouse *mouse;
//+------------------------------------------------------------------+
      struct st00
      {
         bool            ExecStudy,
                         ClearStudy;
         double          MemPrice;
         datetime        MemDT;
         color           corP,
                         corN;
       }m_Info;
//+------------------------------------------------------------------+

在这里,我们声明需要包含在系统中的文件。请注意,显示的路径是相对于C_StudyS2.mqh文件所在的路径。这将使在维护其结构的同时更容易将项目移动到其他目录。接下来,我们将定义一些我们将在研究过程中使用的对象名称。还有一些别名声明可以使编程过程更容易,因为在编写代码时,它们将在许多地方使用。我们在这个片段中看到的最后一件事是结构,它将通过一个私有全局变量访问。

以下是下一个代码部分:

#define def_FontName "Lucida Console"
#define def_FontSize 10
       void GetDimensionText(const string szArg, int &w, int &h)
          {
             TextSetFont(def_FontName, -10 * def_FontSize, FW_NORMAL);
             TextGetSize(szArg, w, h);
             h += 5;
             w += 5;
          }
//+------------------------------------------------------------------+
       void CreateBTNInfo(int x, int w, int h, string szName, color backColor)
          {
             (*mouse).CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_STATE, true);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BORDER_COLOR, clrBlack);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_COLOR, clrBlack);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BGCOLOR, backColor);
             ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_FONT, def_FontName);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_FONTSIZE, def_FontSize);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XDISTANCE, x);
          }
#undef def_FontSize
#undef def_FontName

这里我们有两个声明,它们将只在这个位置使用,所以我们定义并在代码的其余部分不再需要定义时删除它。在这一点上,我们已经在调用一个函数,该函数创建要在图表上使用的对象。任务是创建一个对象,然后调整其某些属性,使其以您想要的方式构建。但是,如果您注意到,我们使用该按钮,就好像它是一个具有只读文本的窗口一样。也许在此处使用OBJ_LABELOBJ_EDIT更合适。然而,我们在这里只谈论展示实现最合适结果的方法之一。因此,我们可以使用另一个对象将数据放在图表上。

这个类的好处在于它包含的两个函数,第一个如下图所示,另一个将在文章末尾讨论。现在让我们看看这个类是如何创建视频01中显示的分析的,它使用斐波那契对象。创建此对象的代码如下所示:

void CreateStudy(void)
   {
      const double FiboLevels[] = {0, 0.236, 0.382, 0.50, 0.618, 1, 1.618, 2};
      ENUM_LINE_STYLE ls;
      color cor;
                                
      ObjectDelete(def_InfoTerminal.ID, def_ExpansionFibo);
      ObjectDelete(def_InfoTerminal.ID, "MOUSE_TB");
      ObjectDelete(def_InfoTerminal.ID, "MOUSE_TI");
      ObjectDelete(def_InfoTerminal.ID, "MOUSE_TT");
      (*mouse).CreateObjectGraphics(def_ExpansionFibo, OBJ_FIBO, clrNONE);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_HIDDEN, false);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_RAY_LEFT, false);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELS, ArraySize(FiboLevels));
      for (int c0 = 0, c1 = ArraySize(FiboLevels); c0 < c1; c0++)
      {
         ls = ((FiboLevels[c0] == 0) || (FiboLevels[c0] == 1) || (FiboLevels[c0] == 2)  ? STYLE_SOLID : STYLE_DASHDOT);
         ls = (FiboLevels[c0] == 0.5 ? STYLE_DOT : ls);
         switch (ls)
         {
            case STYLE_DOT    : cor = clrBlueViolet;  break;
            case STYLE_DASHDOT: cor = clrViolet;      break;
            default           : cor = clrIndigo;
         }
         ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELSTYLE, c0, ls);
         ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELCOLOR, c0, cor);                                  
         ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELWIDTH, c0, 1);
         ObjectSetString(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELTEXT, c0, (string)NormalizeDouble(FiboLevels[c0] * 100, 2));
      }
      ObjectSetDouble(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_PRICE, 1, m_Info.MemPrice = def_InfoMousePos.Price);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_TIME, 1, m_Info.MemDT = def_InfoMousePos.dt);
      CreateBTNInfo(def_InfoMousePos.X, 50, 18, def_ExpansionBtn1, clrNONE);
      m_Info.ExecStudy = true;
      m_Info.ClearStudy = false;
   }

尽管这段代码乍一看可能很复杂,但它实际上由三个部分组成。在这些部分中,我们使用OBJ_FIBO进行特定分析。

  1. 在第一部分中,当从平台接收到指示用户已开始分析交易品种图表的事件时,我们删除了C_Mouse类创建的“不需要的”对象。删除这些对象时,请注意不要删除任何真正重要的内容。因此,我们可以通过删除我们将在这里创建的研究中不需要看到的所有内容来创建一个非常特殊的分析器。请注意,我们已经删除了旧分析的对象。这样做是为了我们能够根据具体标准进行分析。这也可能是因为我们希望使用一个关键组合来创建基于OBJ_FIBO变体的分析。这些变化可以是OBJ_FIBOTIMESOBJ.FIBOFANOBJ-FIBOARCOBJ_FIBOCHANNELOBJ_EXPANSION。它们都遵循相同的原则,如这里所示。
  2. 在第二部分中,我们创建并定义对象的属性。这里有一些有趣的地方:正是在这个阶段,我们告诉平台该对象将在对象列表中可见。在这里,我们指定对象将包含哪些级别。我们在这里使用了静态级别,但是您可以在系统中使用动态或其他级别。在本节中,我会告诉你所有的级别是什么样的,无论是在颜色上还是在用于构建它们的线条形式上。我们可以自行决定进行更改,以获得适当的表现形式,因为当我们进行分析时,我们希望快速理解它,以便从中受益。
  3. 在第三部分也是最后一部分,我们开始直接在图表上构建对象,也就是说,我们开始绘制。我们还将看看变量会发生什么,这是必要的,以便在我们稍后将看到的函数过程中,一切都能正确完成。

基本上,通过这种方式,我们将在已经创建和测试的系统的基础上创建一个研究,该系统位于C_Mouse类中。换句话说,我们不会从头开始构建东西,而是重用和调整我们已经拥有的东西,以实现不同的东西。当我们研究第二个过程时,一切都会变得更清楚。让我们继续看类构造函数和析构函数。您可以在下面看到它们:

C_StudyS2(C_Mouse *arg, color corP, color corN)
   {                               
      mouse = arg;
      ZeroMemory(m_Info);
      m_Info.corP = corP;
      m_Info.corN = corN;
   }
//+------------------------------------------------------------------+
~C_StudyS2()
   {
      ObjectsDeleteAll(def_InfoTerminal.ID, def_ExpansionPrefix);
   }

仔细观察这两个函数,目标是使用基于第二种方式的系统。要使用继承模型,需要添加第一个路径主题中使用的行。同样的事情也需要在类的最后一个函数中完成,此部分实现与平台的交互。以下是该函数的完整代码,它实际上允许您创建分析:

virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      double v1, v2;
      int w, h;
      string sz1;
                                
      switch (id)
      {
         case CHARTEVENT_KEYDOWN:
            if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE) && (m_Info.ExecStudy)) m_Info.ClearStudy = true;
            break;
         case CHARTEVENT_MOUSE_MOVE:
            if (mouse.GetInfoMouse().ExecStudy)
            {
               if (!m_Info.ExecStudy) CreateStudy();
               v1 = def_InfoMousePos.Price - m_Info.MemPrice;
               v2 = MathAbs(100.0 - ((m_Info.MemPrice / def_InfoMousePos.Price) * 100.0));
               sz1 = StringFormat(" %." + (string)def_InfoTerminal.nDigits + "f [ %d ] %02.02f%% ", MathAbs(v1), Bars(def_InfoTerminal.szSymbol, PERIOD_CURRENT, m_Info.MemDT, def_InfoMousePos.dt) - 1, v2);
               GetDimensionText(sz1, w, h);
               ObjectSetDouble(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_PRICE, 0, def_InfoMousePos.Price);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_TIME, 0, def_InfoMousePos.dt);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_COLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
               ObjectSetString(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_TEXT, sz1);                                                                                                                             
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_XSIZE, w);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_YSIZE, h);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_XDISTANCE, def_InfoMousePos.X - w);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, def_InfoMousePos.Y - (v1 < 0 ? 1 : h));
            }else if (m_Info.ExecStudy)
            {
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_COLOR, clrNONE);
               ObjectDelete(def_InfoTerminal.ID, def_ExpansionBtn1);
               if (m_Info.ClearStudy) ObjectDelete(def_InfoTerminal.ID, def_ExpansionFibo);
               m_Info.ExecStudy = false;
            }
            break;
         }
      }

这个代码很有趣,不是吗?请注意,我们没有考虑鼠标动作。我们只关注C_Mouse类正在做什么。当C_Mouse类指示我们正在进行分析时,该类将遵循该方向,从而按照C_Mouse类别的指示执行分析。一旦C_Mouse类不再用于分析,我们将删除用于承载信息文本的对象。如果在分析过程中按ESC键,分析对象也将被删除。用于显示文本的对象的大小是动态计算的,这意味着它可以根据情况而变大或变小,所有这些都在该代码中进行控制。在这里,我们还可以控制对象的颜色和位置。

这段代码有一个有趣的部分,值得更多的解释,让我们接下来看看。我们需要了解我们所代表的是什么,以及为什么要使用这些值。

v1 = def_InfoMousePos.Price - m_Info.MemPrice;
v2 = MathAbs(100.0 - ((m_Info.MemPrice / def_InfoMousePos.Price) * 100.0));
sz1 = StringFormat(" %." + (string)def_InfoTerminal.nDigits + "f [ %d ] %02.02f%% ", MathAbs(v1), Bars(def_InfoTerminal.szSymbol, PERIOD_CURRENT, m_Info.MemDT, def_InfoMousePos.dt) - 1, v2);

要理解这3行,即表示信息的因子和格式,您需要看到系统动态地适应代码运行的符号。一些交易品种可能需要4个字符来表示值,而其他交易品种可能需要5个字符。在一些股票工具中,我们只有两个字符。为了使系统易于适应,我们使用上面的代码。这听起来很奇怪,但实际上很不寻常。

首先,我们考虑分析开始时的价格与价格线之间的差异。这提供了以点数或金融价值为单位的价值;这是我们开始分析的位置和鼠标所在的当前位置之间的偏移值。为了正确地表示它,我们需要知道需要多少个字符。为此,我们使用以下方法。使用百分比符号(%),可以定义将转换为字符串的信息类型。使用以下格式<%.2f>,我们得到一个包含两位小数的值,如果它是<%.4f>,则得到一个含有4位小数的数值,依此类推。但我们需要在运行时中对此进行定义。 

StringFormat函数将自己创建适当的格式。我知道这看起来很混乱,但一旦我们使用差值计算的值被放置,它将完全按照我们创建的格式放置。这将为我们提供与显示值相对应的小数位数。为了理解这在实践中是如何工作的,您需要在具有不同字符数的资产上使用相同的代码,以使其更清晰。另一个问题是用一种简单的方法找出指定点的柱数。

一些平台提供了一个指标,用于统计柱数并将其显示在图表上。我们可以很容易地创建这样一个指标。然而,与此同时,越来越多的信息会出现在图表上,这往往会使阅读变得困难,因为图表最终充满了大量的信息,其中大部分往往是不必要的。以一种稍微更奇特的方式使用MQL5语言,我们可以计算分析区域中有多少个柱,并直接在图表上实时显示该值。一旦分析完成,只有我们需要的信息才会留在图表上。

为了进行这样的分析,我们使用具有这些参数的函数。但要小心,如果在没有柱形的区域进行分析,则指标值将为-1,如果在一个柱上进行分析,该值将为零。为了改变这一点,由于柱形的数量指的是分析区域中的柱形数量,我们只需删除-1的值。因此,该值将始终对应于柱的实际数量,包括开始分析的柱。这就是为什么有时分析会得出-1的值。

由于我们还想报告显示的偏差百分比,我们使用此计算来获得此百分比。为了使可视化更容易理解,我们使用这种格式,以便百分比符号可以与其他信息一起显示。


结论

在这里,我展示了一种在您的专业编程生活中的不同阶段都非常有用的技术。我已经表明,与许多人的想法相反,受限制的不是平台本身,而是那些认为平台或语言不允许你创造不同事物的人的知识。我在这里所解释的证明,凭借常识和创造力,MetaTrader 5平台可以变得更加有趣和通用。你不必创建疯狂的程序或类似的东西。您可以创建简单、安全和可靠的代码。利用你的创造力来修改现有的代码,而不需要在源代码中删除或添加任何一行。因此,如果在某个时刻,你已经使用了一段时间的代码真的派上了用场,你可以不断地、毫不费力地将其添加到你的健壮代码中。这就是为什么使用类的概念,其中只需创建代码继承层次结构。

没有不能完成的工作,只有某些人不能完成的工作,但这并不意味着该任务不能执行。

在附件中,您将找到我们在这些文章中创建的完整代码。在下一部分中,我们将更详细地研究这个系统,但没有分析代码。也许它的一部分将被构建到C_Mouse类中。如果发生这种情况,我就不详细介绍了,因为所有的代码都已经在以前的文章中解释过了。期待很快与您见面。


本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/11372

附加的文件 |
Files_-_BOLSA.zip (1358.24 KB)
Files_-_FOREX.zip (3743.96 KB)
Files_-_FUTUROS.zip (11397.51 KB)
开发回放系统 — 市场模拟(第 26 部分):智能交易系统项目 — C_Terminal 类 开发回放系统 — 市场模拟(第 26 部分):智能交易系统项目 — C_Terminal 类
现在,我们可以开始创建回放/模拟系统的智能系统。不过,我们需要改进一些东西,并非敷衍了事。尽管如此,我们不应被最初的复杂性所吓倒。重要的是从某处开始,否则我们最终只会空想一项任务的难度,甚至没有尝试去克服它。这就是编程的全部意义:通过学习、测试和广泛的研究来攻克障碍。
神经网络变得简单(第 56 部分):利用核范数推动研究 神经网络变得简单(第 56 部分):利用核范数推动研究
强化学习中的环境研究是一个紧迫的问题。我们之前已视察过一些方式。在本文中,我们将讲述另一种基于最大化核范数的方法。它允许智能体识别拥有高度新颖性和多样性的环境状态。
神经网络变得简单(第 57 部分):随机边际扮演者-评论者(SMAC) 神经网络变得简单(第 57 部分):随机边际扮演者-评论者(SMAC)
在此,我将研究相当新颖的随机边际扮演者-评论者(SMAC)算法,该算法允许在熵值最大化的框架内构建潜在变量政策。
MQL5中的范畴论(第23部分):对双重指数移动平均的不同看法 MQL5中的范畴论(第23部分):对双重指数移动平均的不同看法
在这篇文章中,我们继续我们的主题,最后是从“新”的角度处理日常交易指标。我们正在为这篇文章处理自然变换的水平组合,而这方面的最佳指标是双重指数移动平均(DEMA),它扩展了我们刚刚涵盖的内容。