English 中文 Español Deutsch 日本語 Português
preview
Разработка системы репликации (Часть 51): Все усложняется (III)

Разработка системы репликации (Часть 51): Все усложняется (III)

MetaTrader 5Примеры | 22 октября 2024, 10:09
554 1
Daniel Jose
Daniel Jose

Введение

В предыдущей статье Разработка системы репликации (Часть 50): Все усложняется (II) мы начали модифицировать индикатор управления дальше, пока не добились того, чтобы он оставался в пределах графика. Но не любого графика, а того, который был открыт сервисом репликации/моделирования. Главное, что там было реализовано, - это возможность использовать личный шаблон, а не тот, который требует система.

Разрешение такого рода вещей делает использование всей системы репликации/моделирования гораздо более приятным и подходящим для тех, кто действительно хочет использовать систему для получения каких-то исследований. Можно создать шаблон, использовать его в системе репликации/моделирования, а затем использовать тот же шаблон на реальном счете. По этой причине в системе происходят те изменения, которые мы показываем.

Однако взаимодействие между индикатором управления и пользователем было не очень хорошим. Это связано с тем, что в системе есть несколько дублирующихся моментов. Но главная проблема заключается в том, что система не была настолько безопасной и стабильной, как казалось. Это произошло, потому что на стадии разработки, которая была на первом этапе, не учитывалось, что некоторые пользователи часто пытаются использовать систему не так, как предполагалось. Но с нынешним изменением направления и позиционирования, уровень безопасности и стабильности начнет становиться правильным.

Хотя на данный момент мы все еще имеем дело с нестабильной системой с точки зрения взаимодействия с пользователями, это скоро будет исправлено. Как только это будет сделано, вся система выиграет от этого.

Если вы читали предыдущую статью (я советую вам это сделать), вы заметили, что мы начали использовать индикатор управления непосредственно через сервис. После этого индикатор перестал быть свободно доступным для размещения на любом графике.

И вот, спокойно проанализировав код, я заметил, что мы можем внести некоторые модификации, чтобы сделать все еще лучше. Однако потребуется внести некоторые изменения в другие модули, которые также будут использоваться системой репликации/моделирования. По этой причине в данной статье мы сосредоточимся на объяснении изменений, которые будут осуществлены. Это связано с тем, что некоторые из этих модулей будут доступны пользователю. Поэтому важно, чтобы вы понимали, чем можно и чем нельзя манипулировать, поскольку вы являетесь пользователем данной системы и тем программистом, который следит за ее развитием. Это необходимо для того, чтобы избежать нестабильности в использовании любого из модулей. Помните, что некоторые модули можно использовать на РЕАЛЬНЫХ и ДЕМО-счетах, а не только в системе репликации/моделирования.

Вам может показаться, что изменения, которые вы здесь увидите, не так уж велики, но прежде чем продвигать дальнейшие модификации, нам нужно стабилизировать код. По этой причине статья может показаться размытой. Но повторяю: вы должны понимать, как развивается система. Если это не будет понято вами, то вы не сможете правильно пользоваться системой.

Без лишних слов, давайте перейдем к модификациям.


Широкое использование модулей

Первое изменение мы должны внести в исходный код индикатора управления. В коде, который рассматривался до предыдущей статьи, не использовались некоторые модули, которые уже были разработаны и созданы. Из-за этого возникало некоторое несоответствие между тем, что происходило на индикаторе управления и в других модулях системы. 

Одним из таких модулей является индикатор мыши. Данный модуль был разработан для того, чтобы аккумулировать всё, связанное с мышью. Таким образом, любой другой разрабатываемый код не будет нуждаться в определенном тестировании и анализе. Всё это будет делать модуль индикатора мыши.

Данный показатель был разработан некоторое время назад в этой же серии статей о системе репликации/моделирования. Однако изначально он был разработан для работы с реальным или демо-счетом и недостаточно приспособлен для работы в том виде, в котором мы работаем сейчас, но об этом мы еще поговорим подробнее.

В приведенном ниже коде можно увидеть, как интегрировать модуль индикатора мыши с индикатором управления, в который уже внесли некоторые исправления и изменения для обеспечения такой интеграции.

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/ru/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 получают значение. Обычно мы не передаем этому вызову никаких значений. Так почему мы делаем это сейчас? Причина в том, что значение ID графика отличается.

В предыдущей статье я объяснил данную разницу. Однако, возможно, это осталось недостаточно понятным, так что давайте закрепим полученные знания. Основная проблема с ID графика заключается не в том, кто будет открывать график, а в том, кто будет устанавливать на нем объекты. Давайте спокойно разберемся с этим, чтобы понять, почему иногда нам нужно передавать значение в вызов ChartRedraw, а в другое время это необязательно.

Когда сервис открывает график (хотя это может быть что угодно), этот график получает ID от MetaTrader 5. Если вы проверите данный идентификатор, то заметите некое значение при анализе значения, возвращаемого ChartOpen. Хорошо. Теперь, чтобы разместить, например, индикатор на этом графике, программа должна использовать этот же идентификатор, который будет возвращен функцией ChartOpen. Пока всё ясно, но именно в этот момент возникает проблема.

Когда вы, как пользователь, поместите тот же индикатор на график, вы не получите тот же самый идентификатор, который программа получила с помощью ChartOpen. Теперь всё кажется запутанным. Даже может показаться, что я сошел с ума или не понимаю, о чем говорю. Когда сервис открывает график, мы получим значение ID через ChartOpen, и мы должны использовать данное значение для создания хэндла индикатора, чтобы поместить индикатор на нужный график. Если индикатор, как в случае с индикатором управления, использует функции для размещения объектов на графике с помощью вызова ObjectCreate, нам нужно будет указать идентификатор, чтобы MetaTrader 5 знал, какой график является правильным.

Предоставляемый идентификатор НЕ должен, повторяю, НЕ должен быть идентификатором, полученным с помощью ChartID, если график был открыт с помощью ChartOpen. Если мы используем идентификатор графика, указанный в ChartID, то в вызове ObjectCreate на графике, открытом с помощью ChartOpen, объект не будет отображен на графике. Но если этот же код с помощью функции ObjectCreate будет размещен на графике пользователем или шаблоном, то в качестве ID необходимо использовать значение, предоставленное ChartID.

Я знаю, что это кажется очень запутанным, но это именно то, что нужно. По этой причине несколько статей назад класс 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/ru/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 значения, которые в основном были цветами, используемыми в индикаторе мыши, но теперь конструктор получит два дополнительных значения. Первое значение - это идентификатор графика, а второе - название индикатора. Это название будет использоваться для последующего доступа к буферу индикатора. Поэтому всякий раз, когда индикатор мыши находится на графике и мы хотим прочитать его буфер, используем название, указанное здесь.

В коде есть еще несколько отличий, но поскольку они не сложны для понимания, мы не будем разбираться в них. Затем мы можем перейти к коду класса 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): Проект советника — класс C_Mouse (V), вы можете использовать данную статью, чтобы перейти к первой статье об этом индикаторе мыши, так как в начале статьи есть ссылка, как и во всех моих статьях, так что вы можете вернуться назад и проследить эволюцию развития системы.

Если у вас есть сомнения в том, как работает индикатор мыши, или вы хотите адаптировать его под свои личные нужды, вернитесь к тому месту, где я объяснял, как был разработан индикатор. Попробуйте понять, как это работает, а затем вернитесь к этому месту и добавьте показанные модификации. И, конечно же, вместе с тем, что вам нужно и интересно поместить в индикатор мыши. Если вы всё сделаете правильно, то сможете использовать свой собственный индикатор мыши в этой системе репликации/моделирования, которую мы рассмотрим.

Вот совет для тех, кто действительно хочет научиться программировать: попробуйте добавить новую функциональность к индикатору мыши и используйте ее в системе репликации/моделирования. Но не забудьте указать источник своих знаний, что принесет мне большую радость. Это не только мотивирует меня, но также мне нравится показывать то, как я добиваюсь решения проблем, которые многие считают непреодолимыми.

Но вернемся к нашему объяснению кода. В конструкторе класса C_Mouse можно заметить, что в строке 125 мы выполняем инициализацию класса C_Terminal. Значение, которое будет использоваться при инициализации, сообщается в коде индикатора, во входе с названием user00. Если пользователь оставит данный вход нетронутым, класс C_Terminal определит ID графика и при необходимости переключится на использование этого идентификатора, чтобы поместить объекты на нужный график.

Кроме того, строка 127 блокирует индикатор, чтобы он не мог быть установлен на графике более одного раза. Это не позволит пользователю поместить на график другой индикатор мыши, поскольку он будет иметь доступ к этому индикатору. Он будет присутствовать в списке индикаторов MetaTrader 5.

Остальной код работает так же, как и раньше. Прежде, чем вы начнете задавать вопросы при просмотре кода следующих модулей: данный индикатор мыши будет отвечать за то, чтобы указывать MetaTrader 5 на события мыши и отправлять их на график, где расположен индикатор. Обратите внимание на это: для получения событий мыши в MetaTrader 5 необходимо, чтобы индикатор мыши находился на графике; пытаться получить доступ к индикатору с другого графика не имеет смысла. Даже если у нас есть возможность прочитать то, что делает индикатор мыши на другом графике, MetaTrader 5 не будет генерировать события мыши для графика, не содержащего такого индикатора.

За это отвечает строка 131. Таким образом, любой советник или другой индикатор будет получать события мыши до тех пор, пока индикатор находится на графике. И это будет продолжаться пока индикатор мыши находится на графике. Запомните этот факт, потому что следующие коды потребуют присутствия индикатора мыши на графике, чтобы события, связанные с мышью, были переданы 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)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Jeannette Sauve
Jeannette Sauve | 6 нояб. 2024 в 10:03
Это все для биткоин-банкоматов Джиннет, которые разрабатываются, программируются и устанавливаются в Ледуке? 🙂👍
Поиск произвольных паттернов валютных пар на Python с использованием MetaTrader 5 Поиск произвольных паттернов валютных пар на Python с использованием MetaTrader 5
Есть ли повторяющиеся паттерны и закономерности на валютном рынке? Я решил создать свою собственную систему анализа паттернов, используя Python и MetaTrader 5. Этакий симбиоз математики и программирования для покорения Форекса.
Высокочастотная арбитражная торговая система на Python с использованием MetaTrader 5 Высокочастотная арбитражная торговая система на Python с использованием MetaTrader 5
Создаем легальную в глазах брокеров арбитражную систему, которая создает тысячи синтетических цен на рынке Форекс, анализирует их, и успешно торгует в прибыль.
Разработка системы репликации (Часть 52): Всё усложняется (IV) Разработка системы репликации (Часть 52): Всё усложняется (IV)
В этой статье мы изменим указатель мыши, чтобы иметь возможность взаимодействовать с индикатором управления, поскольку он работает нестабильно.
Разработка системы репликации (Часть 50): Все усложняется (II) Разработка системы репликации (Часть 50): Все усложняется (II)
Мы решим проблему ID графиков, но в то же время начнем обеспечивать пользователю возможность использования личного шаблона, ориентированного на анализ того актива, который он хочет изучить и смоделировать. Представленные здесь материалы носят исключительно дидактический характер, ни в коем случае нельзя рассматривать их как приложение с никакой иной целью, кроме изучения и освоения представленных концепций.