English Русский Español Deutsch 日本語 Português
preview
开发回放系统(第 51 部分):事情变得复杂(三)

开发回放系统(第 51 部分):事情变得复杂(三)

MetaTrader 5示例 |
301 0
Daniel Jose
Daniel Jose

概述

在上一篇文章开发回放系统(第 50 部分):事情变得复杂(二)中,我们开始进一步修改控制指标,以确保它保持在图表限制范围内。不在任何图表内,而是由回放/模拟器服务打开的图表。那里实现的主要功能是使用自定义模板的能力,而不是系统所需的模板。

允许这种事情使得使用整个回放/模拟器系统变得更加愉快,并且适合那些真正想要使用该系统进行一些研究的人。您可以创建一个模板,在回放/模拟器系统中使用它,然后在真实帐户上使用相同的模板。因此,我们现在正在修改系统。

但控制指标与用户的交互不太好,这是因为系统中存在多个重复的地方。但主要问题是该系统并不像看上去那么安全和稳定。发生这种情况的原因是,开发阶段(即第一阶段)没有考虑到某些用户可能会尝试以非预期的方式使用系统这一情况。但随着当前方向和定位的改变,安全稳定水平将开始提高。

虽然我们目前仍在处理用户体验方面不稳定的系统,但很快就会得到解决。一旦完成,整个系统都将从中受益。

如果您阅读了上一篇文章(我建议您阅读),您就会知道我们已经开始直接通过服务使用控制指标。此后,该指标不再可自由放置在任何图表上。

冷静分析代码后,我发现我们可以做一些修改,使其变得更好。然而,回放/模拟器系统所使用的其他模块也需要进行一些修改。因此,在本文中我们将重点解释将实现的修改。这是因为其中一些模块将可供用户使用。因此,了解什么是可以操纵的,什么是不能操纵的,这一点很重要,因为你是这个系统的用户,也是监控其开发的程序员。这是避免任何模块使用不稳定所必需的。请记住,某些模块不仅可以在回放/模拟器系统中使用,还可以在真实和模拟帐户中使用。

您可能认为这里看到的修改并不大,但在进行进一步的修改之前,我们需要确保代码稳定。因此,这篇文章可能看起来有点模糊不清。但我再重复一遍:你必须了解系统是如何开发的,如果您不了解这一点,您将无法正确使用该系统。

不用多说了,让我们开始修改吧。


扩展模块的使用

我们需要做的第一个修改是控制指标的源代码。上一篇文章之前讨论的代码没有使用一些已经开发和创建的模块。这导致控制指标和系统其他模块上发生的事情出现一些不一致。 

其中一个模块是鼠标指标。开发此模块是为了集合与鼠标相关的所有内容。这样,任何其他新代码就不需要任何特别的测试和分析。这一切都将由鼠标指标模块完成。

这个指标是在不久前关于回放/模拟器系统的系列文章中开发的。但是,它最初是设计用于真实账户或模拟账户的,不太适合以当前形式工作,但我们稍后会更详细地讨论这一点。

在下面的代码中,您可以看到如何将鼠标指标模块与控制指标集成,该模块已经进行了一些修复和更改以允许这种集成。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.51"
07. #property link "https://www.mql5.com/en/articles/11877"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. C_Controls *control = NULL;
14. //+------------------------------------------------------------------+
15. input long user00 = 0;   //ID
16. //+------------------------------------------------------------------+
17. int OnInit()
18. {
19.     u_Interprocess Info;
20. 
21.     ResetLastError();       
22.     if (CheckPointer(control = new C_Controls(user00, "Market Replay Control", "Indicator Mouse Study")) == POINTER_INVALID)
23.             SetUserError(C_Terminal::ERR_PointerInvalid);
24.     if (_LastError != ERR_SUCCESS)
25.     {
26.             Print("Control indicator failed on initialization.");
27.             return INIT_FAILED;
28.     }       
29.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
30.     EventChartCustom(user00, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
31.     (*control).Init(Info.s_Infos.isPlay);
32.     
33.     return INIT_SUCCEEDED;
34. }
35. //+------------------------------------------------------------------+
36. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
37. {
38.     static bool bWait = false;
39.     u_Interprocess Info;
40.     
41.     Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
42.     if (!bWait)
43.     {
44.             if (Info.s_Infos.isWait)
45.             {
46.                     EventChartCustom(user00, C_Controls::ev_WaitOn, 1, 0, "");
47.                     bWait = true;
48.             }
49.     }else if (!Info.s_Infos.isWait)
50.     {
51.             EventChartCustom(user00, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
52.             bWait = false;
53.     }
54. 
55.     return rates_total;
56. }
57. //+------------------------------------------------------------------+
58. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
59. {
60.     (*control).DispatchMessage(id, lparam, dparam, sparam);
61. }
62. //+------------------------------------------------------------------+
63. void OnDeinit(const int reason)
64. {
65.     switch (reason)
66.     {
67.             case REASON_TEMPLATE:
68.                     Print("Modified template. Replay/simulation system shutting down.");
69.             case REASON_PARAMETERS:
70.             case REASON_REMOVE:
71.             case REASON_CHARTCLOSE:
72.                     if (ChartSymbol(user00) != def_SymbolReplay) break;
73.                     GlobalVariableDel(def_GlobalVariableReplay);
74.                     ChartClose(user00);
75.                     break;
76.     }
77.     delete control;
78. }
79. //+------------------------------------------------------------------+

控制指标源代码

您可能已经注意到,该代码与之前的代码有很大不同。但最明显的区别是在 OnInit 函数中,在第 22 行,我们对控制类的引用有一个与前一个完全不同的声明。

如果您查看并尝试弄清楚第 22 行的作用,您可能会感到有些迷茫。但在这一行,我们做了两件事:

  • 首先,我们将控制指标转换成一个模块;任何其他需要知道该指标功能的模块都可以使用这个模块。
  • 第二,我们告诉控制指标,我们将不再使用代码来分析鼠标。换句话说,控制指标现在会询问鼠标指标用户正在做什么或将要做什么。根据这些信息,控制指标将执行适当的程序。

这一改变或决定,让鼠标指标负责用户、鼠标和图形之间的交互,无疑是实现这一目标的最佳方式。重复的事情是没有意义的,因为我们解决了鼠标指标中的问题,但却在控制指标中产生了问题,当我们将它们放在一起时,一个会干扰另一个。这不是我们想要的。我们希望它们两者能够和谐地工作。这样,我们就不会浪费时间进行修改,因为只需编辑交互的方式就足够了。

现在,如果你注意的话,你会注意到控制指标不再像以前那样提及 C_Terminal 类。为什么呢?原因就是继承。我决定将控制类设为 C_Terminal 类的子类。现在解释这一行动的原因有点困难,但为了我们以后要做的事情,这是必要的。

在牢记对指标代码的首次解释之后,让我们看一下控制类代码。

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\Interprocess.mqh"
005. //+------------------------------------------------------------------+
006. #define def_PathBMP           "Images\\Market Replay\\Control\\"
007. #define def_ButtonPlay        def_PathBMP + "Play.bmp"
008. #define def_ButtonPause       def_PathBMP + "Pause.bmp"
009. #define def_ButtonLeft        def_PathBMP + "Left.bmp"
010. #define def_ButtonLeftBlock   def_PathBMP + "Left_Block.bmp"
011. #define def_ButtonRight       def_PathBMP + "Right.bmp"
012. #define def_ButtonRightBlock  def_PathBMP + "Right_Block.bmp"
013. #define def_ButtonPin         def_PathBMP + "Pin.bmp"
014. #define def_ButtonWait        def_PathBMP + "Wait.bmp"
015. #resource "\\" + def_ButtonPlay
016. #resource "\\" + def_ButtonPause
017. #resource "\\" + def_ButtonLeft
018. #resource "\\" + def_ButtonLeftBlock
019. #resource "\\" + def_ButtonRight
020. #resource "\\" + def_ButtonRightBlock
021. #resource "\\" + def_ButtonPin
022. #resource "\\" + def_ButtonWait
023. //+------------------------------------------------------------------+
024. #define def_PrefixObjectName    "Market Replay _ "
025. #define def_NameObjectsSlider   def_PrefixObjectName + "Slider"
026. #define def_PosXObjects         120
027. //+------------------------------------------------------------------+
028. #include "..\Auxiliar\C_Terminal.mqh"
029. #include "..\Auxiliar\C_Mouse.mqh"
030. //+------------------------------------------------------------------+
031. class C_Controls : private C_Terminal
032. {
033.    protected:
034.            enum EventCustom {ev_WaitOn, ev_WaitOff};
035.    private :
036. //+------------------------------------------------------------------+
037.            string  m_szBtnPlay;
038.            bool    m_bWait;
039.            struct st_00
040.            {
041.                    string  szBtnLeft,
042.                            szBtnRight,
043.                            szBtnPin,
044.                            szBarSlider,
045.                            szBarSliderBlock;
046.                    int     posPinSlider,
047.                            posY,
048.                            Minimal;
049.            }m_Slider;
050.            C_Mouse *m_MousePtr;
051. //+------------------------------------------------------------------+
052. inline void CreateObjectBitMap(int x, int y, string szName, string Resource1, string Resource2 = NULL)
053.                    {
054.                            ObjectCreate(GetInfoTerminal().ID, szName, OBJ_BITMAP_LABEL, 0, 0, 0);
055.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, def_PosXObjects + x);
056.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, y);
057.                            ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_BMPFILE, 0, "::" + Resource1);
058.                            ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_BMPFILE, 1, "::" + (Resource2 == NULL ? Resource1 : Resource2));
059.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_ZORDER, 1);
060.                    }
061. //+------------------------------------------------------------------+
062. inline void CreteBarSlider(int x, int size)
063.                    {
064.                            ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJ_RECTANGLE_LABEL, 0, 0, 0);
065.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
066.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Slider.posY - 4);
067.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
068.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
069.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
070.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
071.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
072.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
073.                            ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJ_RECTANGLE_LABEL, 0, 0, 0);
074.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
075.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Slider.posY - 9);
076.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
077.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
078.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
079.                    }
080. //+------------------------------------------------------------------+
081.            void CreateBtnPlayPause(bool state)
082.                    {
083.                            m_szBtnPlay = def_PrefixObjectName + "Play";
084.                            CreateObjectBitMap(0, 25, m_szBtnPlay, (m_bWait ? def_ButtonWait : def_ButtonPause), (m_bWait ? def_ButtonWait : def_ButtonPlay));
085.                            ObjectSetInteger(GetInfoTerminal().ID, m_szBtnPlay, OBJPROP_STATE, state);
086.                    }
087. //+------------------------------------------------------------------+
088.            void CreteCtrlSlider(void)
089.                    {
090.                            u_Interprocess Info;
091.                            
092.                            m_Slider.szBarSlider       = def_NameObjectsSlider + " Bar";
093.                            m_Slider.szBarSliderBlock  = def_NameObjectsSlider + " Bar Block";
094.                            m_Slider.szBtnLeft         = def_NameObjectsSlider + " BtnL";
095.                            m_Slider.szBtnRight        = def_NameObjectsSlider + " BtnR";
096.                            m_Slider.szBtnPin          = def_NameObjectsSlider + " BtnP";
097.                            m_Slider.posY = 40;
098.                            CreteBarSlider(77, 436);
099.                            CreateObjectBitMap(47, 25, m_Slider.szBtnLeft, def_ButtonLeft, def_ButtonLeftBlock);
100.                            CreateObjectBitMap(511, 25, m_Slider.szBtnRight, def_ButtonRight, def_ButtonRightBlock);
101.                            CreateObjectBitMap(0, m_Slider.posY, m_Slider.szBtnPin, def_ButtonPin);
102.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnPin, OBJPROP_ANCHOR, ANCHOR_CENTER);
103.                            if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
104.                            m_Slider.Minimal = Info.s_Infos.iPosShift;
105.                            PositionPinSlider(Info.s_Infos.iPosShift);
106.                    }
107. //+------------------------------------------------------------------+
108. inline void RemoveCtrlSlider(void)
109.                    {                       
110.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
111.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_NameObjectsSlider);
112.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
113.                    }
114. //+------------------------------------------------------------------+
115. inline void PositionPinSlider(int p, const int minimal = 0)
116.                    {
117.                            m_Slider.posPinSlider = (p < minimal ? minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
118.                            m_Slider.posPinSlider = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
119.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnPin, OBJPROP_XDISTANCE, m_Slider.posPinSlider + def_PosXObjects + 95);
120.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != minimal);
121.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != m_Slider.Minimal);
122.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnRight, OBJPROP_STATE, m_Slider.posPinSlider < def_MaxPosSlider);
123.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, minimal + 2);
124.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
125.                            ChartRedraw(GetInfoTerminal().ID);
126.                    }
127. //+------------------------------------------------------------------+
128.    public  :
129. //+------------------------------------------------------------------+
130.            C_Controls(const long Arg0, const string szShortName, C_Mouse *MousePtr)
131.                    :C_Terminal(Arg0),
132.                     m_bWait(false),
133.                     m_MousePtr(MousePtr)
134.                    {
135.                            if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
136.                            m_szBtnPlay          = NULL;
137.                            m_Slider.szBarSlider = NULL;
138.                            m_Slider.szBtnPin    = NULL;
139.                            m_Slider.szBtnLeft   = NULL;
140.                            m_Slider.szBtnRight  = NULL;
141.                    }
142. //+------------------------------------------------------------------+
143.            ~C_Controls()
144.                    {
145.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
146.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixObjectName);
147.                    }
148. //+------------------------------------------------------------------+
149.            void Init(const bool state)
150.                    {
151.                            CreateBtnPlayPause(state);
152.                            GlobalVariableTemp(def_GlobalVariableReplay);
153.                            if (!state) CreteCtrlSlider();
154.                            ChartRedraw(GetInfoTerminal().ID);
155.                    }
156. //+------------------------------------------------------------------+
157.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
158.                    {
159.                            u_Interprocess Info;
160.                            
161.                            switch (id)
162.                            {
163.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOn):
164.                                            if (lparam == 0) break;
165.                                            m_bWait = true;
166.                                            CreateBtnPlayPause(true);
167.                                            break;
168.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOff):
169.                                            if (lparam == 0) break;
170.                                            m_bWait = false;
171.                                            Info.df_Value = dparam;
172.                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
173.                                            break;
174.                                    case CHARTEVENT_OBJECT_DELETE:
175.                                            if (StringSubstr(sparam, 0, StringLen(def_PrefixObjectName)) == def_PrefixObjectName)
176.                                            {
177.                                                    if (StringSubstr(sparam, 0, StringLen(def_NameObjectsSlider)) == def_NameObjectsSlider)
178.                                                    {
179.                                                            RemoveCtrlSlider();
180.                                                            CreteCtrlSlider();
181.                                                    }else
182.                                                    {
183.                                                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
184.                                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
185.                                                    }
186.                                                    ChartRedraw(GetInfoTerminal().ID);
187.                                            }
188.                                            break;
189.                            }
190.                    }
191. //+------------------------------------------------------------------+
192. };
193. //+------------------------------------------------------------------+
194. #undef def_PosXObjects
195. #undef def_ButtonPlay
196. #undef def_ButtonPause
197. #undef def_ButtonLeft
198. #undef def_ButtonRight
199. #undef def_ButtonPin
200. #undef def_NameObjectsSlider
201. #undef def_PrefixObjectName
202. #undef def_PathBMP
203. //+------------------------------------------------------------------+

C_Control 类的源代码

您可能不会注意到代码中的任何重大变化。事实上,它们非常微妙。首先,控制类是 C_Terminal 类的私有继承者。这可在第 31 行看到。这样我们就不再需要使用指针来访问 C_Terminal 类。换句话说,由于某些编译细节,对 C_Terminal 类的批量访问可以直接执行,并且在某些情况下速度会更快一些,但这不再重要。

在第 130 行,声明类构造函数的地方,我们发现了一些非常有趣的事情。请注意,在此早期阶段,只有服务才能访问此控制指标。看第 135 行,实际上,这并不是真正必要的,但由于代码重用总是应该的,我们确保指标遵循我们想要的假设。换句话说,每个图表应该只有一个指标。同样,这不是必需的,因为在早期阶段只有服务可以访问该指标并将其添加到图表中。还有其他方法可以做到这一点,但我不会详细说明,因为我不想鼓励任何人在不了解其工作原理的情况下使用该资源。

在这个构造函数中另一件值得一提的是,在第 133 行,我们在一个私有全局类变量中存储了访问鼠标指标的指针。但这以后将会变得清晰。

如果你注意的话,你会看到在这个代码中已经做了修复,这是一个非常微妙的变化,但它带来了很大的不同。它不是为了代码,而是为了 MetaTrader 5。重点是 ChartRedraw 调用会接收一个数值,我们通常不会向此调用传递任何值。那么我们为什么现在要这样做呢?原因是 Chart ID 值不同。

我在上一篇文章中已经解释了这个差异。不过这可能还不够清楚,让我们巩固一下所学到的知识。Chart ID 的主要问题不是谁会打开图表,而是谁会在图表上放置对象。让我们花点时间来了解为什么有时我们需要将值传递到 ChartRedraw 调用中,而有时则不需要。

当服务打开图表(尽管可以是任何对象)时,该图表会从 MetaTrader 5 获取一个 ID。如果您检查此标识符,您将在解析 ChartOpen 返回的值时看到一些值。好的,现在,举个例子,要在此图表上放置一个指标,程序必须使用 ChartOpen 函数返回的相同标识符。到目前为止一切都清楚了,但问题就在这时出现了。

当您作为用户在图表上放置相同的指标时,您将不会获得程序使用 ChartOpen 获得的相同图表 ID。现在一切似乎都很混乱,甚至可能看起来我疯了,或者不明白我在说什么。当服务打开图表时,我们将通过 ChartOpen 获取 ID 值,我们应该使用这个值来创建指标句柄,以将指标放置在所需的图表上。如果指标(与控制指标一样)使用函数通过 ObjectCreate 调用将对象放置在图表上,我们将需要指定一个标识符,以便 MetaTrader 5 知道哪个图表是正确的。

如果图表是通过 ChartOpen 打开的,则提供的 ID 不能是通过 ChartID 获得的 ID。如果我们使用 ChartID 中指定的图表 ID,那么在使用 ChartOpen 打开的图表上调用 ObjectCreate 时,对象将不会显示在图表上。但是如果用户或模板使用 ObjectCreate 函数将相同的代码放置在图表上,则必须使用 ChartID 提供的值作为 ID。

我知道这看起来很令人困惑,但这正是它所需要的。因此,几篇文章之前,我们专门更新了 C_Terminal 类来处理这个问题。

但由于我们的代码不知道究竟是谁在图表上执行了它,我们使用对 C_Terminal 类的调用来返回正确的 ID。这样,ChartRedraw 函数就会按照预期正确地更新图表。

尽管存在这些复杂性,但在第 157 行(消息处理程序开始的位置)中,您可以看到此代码负责处理 MetaTrader 5 报告的事件。我们删除了解析鼠标移动和点击事件的事件。这是因为我们将以稍微不同的方式处理这种事件,尽管是在同一个函数中。 

但是,在开始处理之前,我们需要进行一些额外的更改。但现在它不在这个指标代码中,也不在服务代码中。我们必须返回鼠标指标代码并对其进行一些更改。


更新鼠标指标代码

我们即将进行的更新不会与鼠标指标中已经使用的功能冲突。出于上述原因,我们需要进行此更新。一旦我们运行回放/模拟器服务,它将在图表上启动鼠标指标,以便我们可以与回放/模拟器系统进行交互。但是您不必担心某些事情,因为代码本身会做出必要的更改,以便鼠标指标的工作方式与您(用户)手动或使用模板将其放置在图表上一样。

当我们使用所拥有的工具正确初始化回放/模拟器系统时,大问题就出现了。因此,让我们看一下已更新的第一件事,即鼠标指标源代码。这是其完整代码。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "This is an indicator for graphical studies using the mouse."
04. #property description "This is an integral part of the Replay / Simulator system."
05. #property description "However it can be used in the real market."
06. #property version "1.51"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/en/articles/11877"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. #property indicator_buffers 1
12. //+------------------------------------------------------------------+
13. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
14. //+------------------------------------------------------------------+
15. C_Study *Study      = NULL;
16. //+------------------------------------------------------------------+
17. input long  user00  = 0;                                    //ID
18. input C_Study::eStatusMarket user01 = C_Study::eAuction;    //Market Status
19. input color user02  = clrBlack;                             //Price Line
20. input color user03  = clrPaleGreen;                         //Positive Study
21. input color user04  = clrLightCoral;                        //Negative Study
22. //+------------------------------------------------------------------+
23. C_Study::eStatusMarket m_Status;
24. int m_posBuff = 0;
25. double m_Buff[];
26. //+------------------------------------------------------------------+
27. int OnInit()
28. {
29.     ResetLastError();
30.     Study = new C_Study(user00, "Indicator Mouse Study", user02, user03, user04);
31.     if (_LastError != ERR_SUCCESS) return INIT_FAILED;
32.     if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
33.     {
34.             MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
35.             OnBookEvent((*Study).GetInfoTerminal().szSymbol);
36.             m_Status = C_Study::eCloseMarket;
37.     }else
38.             m_Status = user01;
39.     SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
40.     ArrayInitialize(m_Buff, EMPTY_VALUE);
41.     
42.     return INIT_SUCCEEDED;
43. }
44. //+------------------------------------------------------------------+
45. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
46. {
47.     m_posBuff = rates_total - 4;
48.     (*Study).Update(m_Status);      
49.     
50.     return rates_total;
51. }
52. //+------------------------------------------------------------------+
53. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
54. {
55.     (*Study).DispatchMessage(id, lparam, dparam, sparam);
56.     SetBuffer();
57.     
58.     ChartRedraw((*Study).GetInfoTerminal().ID);
59. }
60. //+------------------------------------------------------------------+
61. void OnBookEvent(const string &symbol)
62. {
63.     MqlBookInfo book[];
64.     C_Study::eStatusMarket loc = m_Status;
65.    
66.     if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
67.     MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
68.     m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading);
69.     for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
70.             if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
71.     if (loc != m_Status) (*Study).Update(m_Status);
72. }
73. //+------------------------------------------------------------------+
74. void OnDeinit(const int reason)
75. {
76.     if (reason != REASON_INITFAILED)
77.     {
78.             if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
79.                     MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
80.             delete Study;
81.     }
82. }
83. //+------------------------------------------------------------------+
84. inline void SetBuffer(void)
85. {
86.     uCast_Double Info;
87.     
88.     m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
89.     m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
90.     Info._datetime = (*Study).GetInfoMouse().Position.dt;
91.     m_Buff[m_posBuff + 1] = Info.dValue;
92.     Info._int[0] = (*Study).GetInfoMouse().Position.X;
93.     Info._int[1] = (*Study).GetInfoMouse().Position.Y;
94.     m_Buff[m_posBuff + 2] = Info.dValue;
95.     Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
96.     m_Buff[m_posBuff + 3] = Info.dValue;
97. }
98. //+------------------------------------------------------------------+

鼠标指标源代码

注意:您可能认为此代码与之前的版本没有什么区别,但其实是有区别的。你必须对它们极其小心。

对于用户来说,主要的区别在第 17 行。这可能是整个代码中最复杂的一行。理想情况下,用户应该永远不能更改此行或在指标界面中看到它。这是因为任何新用户都会倾向于更改此行,因为它是一个输入参数。但是,请记住您绝不能更改此值。该值必须由调用指标的程序或指标本身设置的。但是,作为用户,您绝不应该更改此值。其他值可以由用户配置和更改,不会出现任何问题,但 ID 值永远不能更改。

除此之外,第 30 行的内容很有趣。在这里我们初始化学习类指针。以前构造函数接收 3 个值,主要是鼠标指标中使用的颜色,但现在构造函数将接收两个额外的值。第一个值是图表 ID,第二个值是指标名称。该名称将用于后续对指标缓冲区的访问。因此,每当鼠标指标位于图表上并且我们想要读取其缓冲区时,我们都会使用此处指定的名称。

代码中还有一些差异,但由于它们并不难理解,因此我们不再讨论。然后我们可以继续进行 C_Study 类代码。在这段代码中,我们不会使用指针来访问 C_Terminal 类,而是使用继承。

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\C_Mouse.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ExpansionPrefix "MouseExpansion_"
007. #define def_ExpansionBtn1 def_ExpansionPrefix + "B1"
008. #define def_ExpansionBtn2 def_ExpansionPrefix + "B2"
009. #define def_ExpansionBtn3 def_ExpansionPrefix + "B3"
010. //+------------------------------------------------------------------+
011. class C_Study : public C_Mouse
012. {
013.    private :
014. //+------------------------------------------------------------------+
015.            struct st00
016.            {
017.                    eStatusMarket   Status;
018.                    MqlRates        Rate;
019.                    string          szInfo;
020.                    color           corP,
021.                                    corN;
022.                    int             HeightText;
023.            }m_Info;
024. //+------------------------------------------------------------------+
025.            const datetime GetBarTime(void)
026.                    {
027.                            datetime dt;
028.                            u_Interprocess info;
029.                            int i0 = PeriodSeconds();
030.                            
031.                            if (m_Info.Status == eInReplay)
032.                            {
033.                                    if (!GlobalVariableGet(def_GlobalVariableServerTime, info.df_Value)) return ULONG_MAX;
034.                                    if ((dt = info.ServerTime) == ULONG_MAX) return ULONG_MAX;
035.                            }else dt = TimeCurrent();
036.                            if (m_Info.Rate.time <= dt)
037.                                    m_Info.Rate.time = (datetime)(((ulong) dt / i0) * i0) + i0;
038. 
039.                            return m_Info.Rate.time - dt;
040.                    }
041. //+------------------------------------------------------------------+
042.            void Draw(void)
043.                    {
044.                            double v1;
045.                            
046.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - 18);
047.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - 1);
048.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - 1);
049.                            ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_TEXT, m_Info.szInfo);
050.                            v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / GetInfoMouse().Position.Price) * 100.0), 2);
051.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
052.                            ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
053.                            v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0)) * 100.0), 2);
054.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
055.                            ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
056.                    }
057. //+------------------------------------------------------------------+
058.    public  :
059. //+------------------------------------------------------------------+
060.            C_Study(long IdParam, string szShortName, color corH, color corP, color corN)
061.                    :C_Mouse(IdParam, szShortName, corH, corP, corN)
062.                    {
063.                            if (_LastError != ERR_SUCCESS) return;
064.                            ZeroMemory(m_Info);
065.                            m_Info.Status = eCloseMarket;
066.                            m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1));
067.                            m_Info.corP = corP;
068.                            m_Info.corN = corN;
069.                            CreateObjectInfo(2, 110, def_ExpansionBtn1, clrPaleTurquoise);
070.                            CreateObjectInfo(2, 53, def_ExpansionBtn2);
071.                            CreateObjectInfo(58, 53, def_ExpansionBtn3);
072.                    }
073. //+------------------------------------------------------------------+
074.            ~C_Study()
075.                    {
076.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_ExpansionPrefix);
077.                    }
078. //+------------------------------------------------------------------+
079.            void Update(const eStatusMarket arg)
080.                    {
081.                            datetime dt;
082.                            
083.                            switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
084.                            {
085.                                    case eCloseMarket : m_Info.szInfo = "Closed Market";
086.                                            break;
087.                                    case eInReplay    :
088.                                    case eInTrading   :
089.                                            if ((dt = GetBarTime()) < ULONG_MAX)
090.                                            {
091.                                                    m_Info.szInfo = TimeToString(dt, TIME_SECONDS);
092.                                                    break;
093.                                            }
094.                                    case eAuction     : m_Info.szInfo = "Auction";
095.                                            break;
096.                                    default           : m_Info.szInfo = "ERROR";
097.                            }
098.                            Draw();
099.                    }
100. //+------------------------------------------------------------------+
101. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
102.                    {
103.                            C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
104.                            if (id == CHARTEVENT_MOUSE_MOVE) Draw();
105.                    }
106. //+------------------------------------------------------------------+
107. };
108. //+------------------------------------------------------------------+
109. #undef def_ExpansionBtn3
110. #undef def_ExpansionBtn2
111. #undef def_ExpansionBtn1
112. #undef def_ExpansionPrefix
113. //+------------------------------------------------------------------+

C_Study 类的源代码

此代码与旧代码几乎没有区别。唯一的区别是我们直接在 C_Terminal 类中查找图形标识符。以前我们用指针,现在用继承,但是这个继承从何而来?它来自 C_Mouse 类。让我们看一下 C_Mouse 类的代码以更好地理解这种继承。

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_Terminal.mqh"
005. #include "Interprocess.mqh"
006. //+------------------------------------------------------------------+
007. #define def_MousePrefixName "MouseBase_"
008. #define def_NameObjectLineH def_MousePrefixName + "H"
009. #define def_NameObjectLineV def_MousePrefixName + "TV"
010. #define def_NameObjectLineT def_MousePrefixName + "TT"
011. #define def_NameObjectStudy def_MousePrefixName + "TB"
012. //+------------------------------------------------------------------+
013. class C_Mouse : public C_Terminal
014. {
015.    public  :
016.            enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
017.            enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
018.            struct st_Mouse
019.            {
020.                    struct st00
021.                    {
022.                            int      X,
023.                                     Y;
024.                            double   Price;
025.                            datetime dt;
026.                    }Position;
027.                    uint    ButtonStatus;
028.                    bool    ExecStudy;
029.            };
030. //+------------------------------------------------------------------+
031.    protected:
032.            enum eEventsMouse {ev_HideMouse, ev_ShowMouse};
033. //+------------------------------------------------------------------+
034.            void CreateObjectInfo(int x, int w, string szName, color backColor = clrNONE) const
035.                    {
036.                            if (m_Mem.szShortName != NULL) return;
037.                            CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE);
038.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_STATE, true);
039.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BORDER_COLOR, clrBlack);
040.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_COLOR, clrBlack);
041.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BGCOLOR, backColor);
042.                            ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_FONT, "Lucida Console");
043.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_FONTSIZE, 10);
044.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
045.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, x);
046.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1);
047.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XSIZE, w); 
048.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YSIZE, 18);
049.                    }
050. //+------------------------------------------------------------------+
051.    private :
052.            enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
053.            struct st01
054.            {
055.                    st_Mouse Data;
056.                    color    corLineH,
057.                             corTrendP,
058.                             corTrendN;
059.                    eStudy   Study;
060.            }m_Info;
061.            struct st_Mem
062.            {
063.                    bool     CrossHair,
064.                             IsFull;
065.                    datetime dt;
066.                    string   szShortName;
067.            }m_Mem;
068. //+------------------------------------------------------------------+
069.            void GetDimensionText(const string szArg, int &w, int &h)
070.                    {
071.                            TextSetFont("Lucida Console", -100, FW_NORMAL);
072.                            TextGetSize(szArg, w, h);
073.                            h += 5;
074.                            w += 5;
075.                    }
076. //+------------------------------------------------------------------+
077.            void CreateStudy(void)
078.                    {
079.                            if (m_Mem.IsFull)
080.                            {
081.                                    CreateObjectGraphics(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH);
082.                                    CreateObjectGraphics(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH);
083.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_WIDTH, 2);
084.                                    CreateObjectInfo(0, 0, def_NameObjectStudy);
085.                            }
086.                            m_Info.Study = eStudyCreate;
087.                    }
088. //+------------------------------------------------------------------+
089.            void ExecuteStudy(const double memPrice)
090.                    {
091.                            double v1 = GetInfoMouse().Position.Price - memPrice;
092.                            int w, h;
093.                            
094.                            if (!CheckClick(eClickLeft))
095.                            {
096.                                    m_Info.Study = eStudyNull;
097.                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);
098.                                    if (m_Mem.IsFull) ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T");
099.                            }else if (m_Mem.IsFull)
100.                            {
101.                                    string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ",
102.                                            MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 /memPrice) * 100.0)));
103.                                    GetDimensionText(sz1, w, h);
104.                                    ObjectSetString(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_TEXT, sz1);                                                                                                                          
105.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP));
106.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XSIZE, w);
107.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YSIZE, h);
108.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XDISTANCE, GetInfoMouse().Position.X - w);
109.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - (v1 < 0 ? 1 : h));                           
110.                                    ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price);
111.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
112.                            }
113.                            m_Info.Data.ButtonStatus = eKeyNull;
114.                    }
115. //+------------------------------------------------------------------+
116.    public  :
117. //+------------------------------------------------------------------+
118.            C_Mouse(const long id, const string szShortName)
119.                    :C_Terminal(id)
120.                    {
121.                            m_Mem.szShortName = szShortName;
122.                    }
123. //+------------------------------------------------------------------+
124.            C_Mouse(const long id, const string szShortName, color corH, color corP, color corN)
125.                    :C_Terminal(id)
126.                    {
127.                            if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
128.                            m_Mem.szShortName = NULL;
129.                            if (_LastError != ERR_SUCCESS) return;
130.                            m_Mem.CrossHair = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL);
131.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true);
132.                            ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false);
133.                            ZeroMemory(m_Info);
134.                            m_Info.corLineH  = corH;
135.                            m_Info.corTrendP = corP;
136.                            m_Info.corTrendN = corN;
137.                            m_Info.Study = eStudyNull;
138.                            if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE))
139.                                    CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
140.                    }
141. //+------------------------------------------------------------------+
142.            ~C_Mouse()
143.                    {
144.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, 0, false);
145.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, false);
146.                            ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
147.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName);
148.                    }
149. //+------------------------------------------------------------------+
150. inline bool CheckClick(const eBtnMouse value) 
151.                    {
152.                            return (GetInfoMouse().ButtonStatus & value) == value;
153.                    }
154. //+------------------------------------------------------------------+
155. inline const st_Mouse GetInfoMouse(void)
156.                    {
157.                            if (m_Mem.szShortName != NULL)
158.                            {
159.                                    double Buff[];
160.                                    uCast_Double loc;
161.                                    int handle = ChartIndicatorGet(GetInfoTerminal().ID, 0, m_Mem.szShortName);
162.                                    
163.                                    ZeroMemory(m_Info.Data);
164.                                    if (CopyBuffer(handle, 0, 0, 4, Buff) == 4)
165.                                    {
166.                                            m_Info.Data.Position.Price = Buff[0];
167.                                            loc.dValue = Buff[1];
168.                                            m_Info.Data.Position.dt = loc._datetime;
169.                                            loc.dValue = Buff[2];
170.                                            m_Info.Data.Position.X = loc._int[0];
171.                                            m_Info.Data.Position.Y = loc._int[1];
172.                                            loc.dValue = Buff[3];
173.                                            m_Info.Data.ButtonStatus = loc._char[0];
174.                                            IndicatorRelease(handle);
175.                                    }
176.                            }
177. 
178.                            return m_Info.Data;
179.                    }
180. //+------------------------------------------------------------------+
181. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
182.                    {
183.                            int w = 0;
184.                            static double memPrice = 0;
185.                            
186.                            if (m_Mem.szShortName == NULL) switch (id)
187.                            {
188.                                    case (CHARTEVENT_CUSTOM + ev_HideMouse):
189.                                            if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
190.                                            break;
191.                                    case (CHARTEVENT_CUSTOM + ev_ShowMouse):
192.                                            if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
193.                                            break;
194.                                    case CHARTEVENT_MOUSE_MOVE:
195.                                            ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X = (int)lparam, m_Info.Data.Position.Y = (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
196.                                            if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price));
197.                                            m_Info.Data.Position.dt = AdjustTime(m_Info.Data.Position.dt);
198.                                            ChartTimePriceToXY(GetInfoTerminal().ID, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price, m_Info.Data.Position.X, m_Info.Data.Position.Y);
199.                                            if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
200.                                            m_Info.Data.ButtonStatus = (uint) sparam;
201.                                            if (CheckClick(eClickMiddle))
202.                                                    if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
203.                                            if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
204.                                            {
205.                                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
206.                                                    if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price);
207.                                                    m_Info.Study = eStudyExecute;
208.                                            }
209.                                            if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
210.                                            m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute;
211.                                            break;
212.                                    case CHARTEVENT_OBJECT_DELETE:
213.                                            if ((m_Mem.IsFull) && (sparam == def_NameObjectLineH)) CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
214.                                            break;
215.                            }
216.                    }
217. //+------------------------------------------------------------------+
218. };
219. //+------------------------------------------------------------------+
220. #undef def_MousePrefixName
221. #undef def_NameObjectLineV
222. #undef def_NameObjectLineH
223. #undef def_NameObjectLineT
224. #undef def_NameObjectStudy
225. //+------------------------------------------------------------------+

C_Mouse 类的源代码

与以前一样,此代码与旧代码之间的主要区别在于不再使用指针来访问 C_Terminal 类。该过程的职责可以在第 13 行看到,其中我们从 C_Terminal 类继承到 C_Mouse 类。因此,C_Terminal 类的所有过程和公有函数现在都扩展了 C_Mouse 类。所有代码大部分已在与鼠标指标相关的文章中解释清楚。 

关于这个话题的最后一篇文章是开发回放系统(第 31 部分):EA 项目 - C_Mouse 类 (五),您可以使用本文跳转到有关此鼠标指标的第一篇文章,因为文章开头有一个链接,就像我所有的文章一样,这样您就可以回去跟踪系统开发的演变。

如果您对鼠标指标的工作原理有任何疑问,或者想要根据个人需要进行调整,您可以阅读文章来了解有关指标如何开发的解释。尝试了解它是如何工作的,然后回到这个地方并添加所示的修改。您还可以在鼠标指标中放置您需要的其他东西。如果您所有操作都正确,您将能够在此回放/模拟器系统中使用您自己的鼠标指标。

对于那些真正想学习编程的人来说,这里有一个小建议:尝试为鼠标指标添加新功能,并在回放/模拟器系统中使用它。但别忘了指出你知识的来源,这会给我带来极大的快乐。它不仅激励了我,而且我也喜欢展示我是如何解决许多人认为无法克服的问题的。

但是让我们回到对代码的解释。在 C_Mouse 类的构造函数中,您可以看到在第 125 行我们初始化了 C_Terminal 类。初始化期间要使用的值在指标代码中,在名为 user00 的输入参数中提供。如果用户保持不变此输入,C_Terminal 类将确定图表 ID,并在必要时切换使用此 ID 将对象放置在所需图表上。

此外,第 127 行锁定指标,使得它不能多次放置在图表上。这将阻止用户在图表上放置另一个鼠标指标。该指标将出现在 MetaTrader 5 指标列表中。

其余代码的工作方式与以前相同。该鼠标指标将负责告诉 MetaTrader 5 接收鼠标事件并将其发送到指标所在的图表。请注意,为了在 MetaTrader 5 中接收鼠标事件,鼠标指标必须位于图表上;尝试从另一个图表访问该指标是没有意义的。即使我们有能力读取鼠标指标在另一个图表上的作用,MetaTrader 5 也不会为不包含此类指标的图表生成鼠标事件。

第 131 行负责这个。因此,只要指标在图表上,任何 EA 交易或其他指标都会接收鼠标事件。记住这一事实,因为以下代码将要求鼠标指标出现在图表上,以便 MetaTrader 5 将鼠标事件传递给以特定方式处理它们的其他代码。

在进入下一阶段修改控制指标之前,我认为有必要对实际情况做出更精确的解释。这样你才能正确的理解本文的全部内容。


理解和吸收知识

你可能会认为我在这篇文章中谈论的一切都是无稽之谈,因为你们中的许多人肯定对 MQL5 有很多经验,或者认识一个编码系统专家。但为了让一切都清楚,我将把鼠标指标的可执行文件附加到本文中。这样做是为了简单起见,但是您可以使用本系列中的材料,并且您将获得相同的结果,因为我将完整的代码放在文章中。您可以尝试一下,看看使用以下代码会发生什么,它比回放/模拟器系统简单得多,但工作原理相同。

01. //+------------------------------------------------------------------+
02. #property service
03. #property copyright "Daniel Jose"
04. #property version   "1.00"
05. //+------------------------------------------------------------------+
06. input string user00 = "EURUSD"; // Symbol
07. //+------------------------------------------------------------------+
08. void OnStart()
09. {
10.    long id;
11.    int handle;
12.    
13.    SymbolSelect(user00, true);
14.    id = ChartOpen(user00, PERIOD_M5);
15.    handle = iCustom(ChartSymbol(id), ChartPeriod(id), "\\Indicators\\Replay\\Mouse Study.ex5", id);
16.    ChartIndicatorAdd(id, 0, handle);
17.    IndicatorRelease(handle);
18. 
19.    Print("ID: ", id);
20.     
21.    while ((!_StopFlag) && (ChartSymbol(id) == user00)) Sleep(300);
22.     
23.    ChartClose(id);
24.    SymbolSelect(user00, false);    
25. }
26. //+------------------------------------------------------------------+

测试服务源代码

在第 06 行,我们为用户提供了指定资产的选项。它可用于打开图表,但交易品种必须位于市场观察窗口中。为此,我们使用的是第 13 行。在第 14 行,我们要求 MetaTrader 5 为我们打开一个图表。在第 15 行中,我们创建一个句柄,将指标添加到图表中;在这种情况下,我们将强制 MetaTrader 5 加载鼠标指标。在第 16 行,我们将指标添加到图表中。在第 17 行,我们释放句柄,因为我们不再需要它了。在第 19 行,我们向终端打印一条消息,指示服务打开的图表的 ID。现在,在第 21 行,我们等待用户关闭图表或终止服务。

如果用户关闭图表,第 23 行将不执行任何操作。然而,如果用户终止服务,第23行将关闭之前打开的图表。第 24 行从市场观察窗口中删除了该交易品种。另一点:要删除的交易品种,它不能有任何与之关联的元素。在这种情况下,我们仅使用图表进行测试,因此不会有任何相关内容。

您可能认为这段代码没什么意义。为什么要费心创建这样的东西,并试图解释它是如何工作的?这正是每个人都应该做的:努力探索超出许多人感到舒适的界限。只有这样,你才能真正理解一切是如何运作的。

我希望你能理解这个思路和本质,因为它对以下文章非常重要。您可以观看视频 01 并查看测试。您也可以尝试使用自己的标准亲自测试。我想强调的是:不要仅仅因为某人在你看来更优秀或更有能力就接受他们的“真理”。测试、质疑权威,只有这样,真正的知识才会变得清晰。


视频 01


结论

这篇文章是迄今为止最难的文章之一,这是由于所提供信息的复杂性造成的。虽然编程本身对我来说很简单,但解释正在发生的事情非常困难。原因是它不仅适合知识水平较高的人阅读,而且适合初次尝试了解这个编程世界的爱好者阅读。

在下一篇文章中,我将展示如何在鼠标指标、控制指标、回放/模拟器系统和您(亲爱的系统用户)之间创建交互。回头见!

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

附加的文件 |
Anexo.zip (420.65 KB)
开发Python交易机器人(第三部分):实现基于模型的交易算法 开发Python交易机器人(第三部分):实现基于模型的交易算法
让我们继续阅读关于使用Python和MQL5开发交易机器人系列的文章。在本文中,我们将用Python中创建一个交易算法。
MacOS 上的 MetaTrader 5 MacOS 上的 MetaTrader 5
我们为 macOS 上的 MetaTrader 5 交易平台提供了专用的安装程序。它是一个功能齐全的向导,允许您以本机方式安装应用程序。安装程序执行所有必需的步骤:它识别您的系统,下载并安装最新的 Wine 版本,对其进行配置,然后在其中安装 MetaTrader。所有步骤都在自动模式下完成,您可以在安装后立即开始使用平台。
您应当知道的 MQL5 向导技术(第 16 部分):配合本征向量进行主成分分析 您应当知道的 MQL5 向导技术(第 16 部分):配合本征向量进行主成分分析
本文所见的主成分分析,是数据分析中的一种降维技术,文中还有如何配合本征值和向量来实现它。一如既往,我们瞄向的是开发一个可在 MQL5 向导中使用的原型专业信号类。
开发回放系统(第 50 部分):事情变得复杂 (二) 开发回放系统(第 50 部分):事情变得复杂 (二)
我们将解决图表 ID 问题,同时开始为用户提供使用个人模板对所需资产进行分析和模拟的能力。此处提供的材料仅用于教学目的,不应被视为除学习和掌握所提供概念以外的任何目的的应用。