Español Português
preview
Разработка системы репликации (Часть 41): Начало второй фазы (II)

Разработка системы репликации (Часть 41): Начало второй фазы (II)

MetaTrader 5Примеры | 19 июня 2024, 13:06
422 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье Разработка системы репликации (Часть 40): Начало второй фазы (I)", мы начали создавать индикатор для поддержки системы исследования и мыши. Но прежде чем начать, я хотел бы задать вам вопрос: можете ли вы представить, зачем мы создаем данный индикатор? Что стоит за нашим намерением продемонстрировать, как создать нечто подобное? Или что я пытаюсь сделать: создать индикатор для мыши?

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

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

На самом деле, за началом новой фазы в системе репликации/моделирования стоит скрытая причина. Об этом мы уже говорили в другой статье. Я устал делать все по-старому в MQL5. Мои личные коды следуют определенным правилам. Однако коды, прикреплены к статьям, были другими. Я всегда стараюсь, чтобы они были максимально простыми и понятными, но я постоянно вижу людей, которые говорят что-то о MQL5 или MetaTrader 5.

Я хочу показать, что можно сделать гораздо больше того, чего достигли другие. Так что будьте готовы, больше никаких шуток. С этого момента мы увидим, как на самом деле выглядит мой код. Я не собираюсь демонстрировать что-то конкретное об определенных активах или индикаторах, а буду показывать вам, что MQL5, как и MetaTrader 5, способны на гораздо большее, чем вы думаете.

Начнем со следующего: В предыдущей статье мы упомянули, что предстоит решить один нерешенный вопрос. Давайте решим эту проблему здесь и сейчас.


Модификация класса C_Mouse

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

Для того чтобы мы смогли полностью понять то, что мы собираемся сделать, придется отказаться от тех идей и концепций, которые вы знали о MetaTrader 5 и языке MQL5. Забудьте всё, что вы знали о работе с ними. Давайте подумаем об этом совершенно по-другому. Если мы сможем понять и усвоить это, то сможем выполнять такой тип написания кода, при котором производительность и стабильность нашего кода будут становиться всё выше и выше.

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

И почему это важно в данном случае? Потому что те же понятия, что и в DLL, можно применить и к тому, что мы собираемся показать здесь. На данный момент единственный индикатор, который у нас есть, - это мышь. Таким образом, у нас не будет полного и всестороннего понимания того, что произойдет. Однако, любое грандиозное сооружение всегда начинается с момента закладки первого камня. Мы сделаем это прямо сейчас. Но из-за некоторых проблем нам придется облегчать работу всецело, чтобы не создавать полный хаос.

Если вы прочитаете статью "Разработка системы репликации (Часть 31): Проект советника — класс C_Mouse (V)", вы заметите, что класс C_Mouse, как и класс C_Study, работает определенным образом. Но то, как они работают, НЕ позволяет нам использовать простое программирование в той степени, в которой мы можем воспользоваться преимуществами индикатора, использующего эти классы. Внимание: Я не говорю, что мы не можем извлечь из этого максимум пользы, а лишь то, что это потребует чрезвычайно сложного программирования.

Идея состоит в том, чтобы упростить эту программу. Для этого мы удалим некоторые элементы из класса C_Study и перенесем их в класс C_Mouse. Однако мы также собираемся внести дополнение с точки зрения программирования и структурирования в класс C_Mouse, чтобы облегчить использование индикатора, как мы увидим в следующих статьях.

То, что мы собираемся удалить в классе C_Study, можно увидеть в следующем фрагменте:

01. #property copyright "Daniel Jose"
02. //+------------------------------------------------------------------+
03. #include "..\C_Mouse.mqh"
04. #include "..\..\Auxiliar\C_Mouse.mqh"
05. #include "..\..\Auxiliar\Interprocess.mqh"
06. //+------------------------------------------------------------------+
07. #define def_ExpansionPrefix "MouseExpansion_"
08. #define def_ExpansionBtn1 def_ExpansionPrefix + "B1"
09. #define def_ExpansionBtn2 def_ExpansionPrefix + "B2"
10. #define def_ExpansionBtn3 def_ExpansionPrefix + "B3"
11. //+------------------------------------------------------------------+
12. #define def_AcessTerminal (*Terminal)
13. #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal()
14. //+------------------------------------------------------------------+
15. class C_Study : public C_Mouse
16. {
17.     protected:
18.     private :
19. //+------------------------------------------------------------------+
20.             enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
21. //+------------------------------------------------------------------+
22.             struct st00
23.             {
24.                     eStatusMarket   Status;
25.                     MqlRates        Rate;
26.                     string          szInfo;
27.                     color           corP,
28.                                     corN;
29.                     int             HeightText;
30.             }m_Info;

Фрагмент кода класса C_Study

Давайте рассмотрим следующий фрагмент, поскольку расположение заголовочного файла C_Study.mqh изменилось. Мы заменили строку 04 строкой 03, а поскольку мы больше не ссылаемся на заголовочный файл InterProcess.mqh, строка 05 также была удалена.

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

Код файла C_Mouse.mqh

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

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

Код, удаленный из строки 20 фрагмента класса C_Study, оказался в строке 19 класса C_Mouse, но посмотрите на строку 18 файла C_Mouse.mqh. Мы объявляем публичную часть, но зачем она нам? Причина в том, что без нее любая информация не будет иметь нужного нам уровня доступа. Нам очень нужно, чтобы эти данные стали общедоступными. Обычно мы используем данную часть только один раз, но из-за моей привычки запускать сначала защищенные данные, затем приватные, а в последнюю очередь публичные, вы увидите эту же часть в строке 122. Но это связано с моей манерой программирования, к которой я привык.

В строке 20 мы также имеем еще одно перечисление, которое уже существовало в классе C_Mouse, но не было публичным, а также структуру, которая начинается в строке 21 и продолжается до строки 32. В данном конкретном случае структура не используется ни в одной публичной глобальной переменной. Объявлять общедоступные глобальные переменные в классах - практика не самая хорошая; любая глобальная переменная класса всегда должна быть приватной или защищенной, и никогда - общедоступной.

Начиная с 34-й строки, данные перестают быть общедоступными. Здесь мы имеем защищенную часть, которая обеспечивает защиту данных, функций и процедур. Мы это уже видели в предыдущих статьях, но суть не в этом. Проблема заключается в тесте в строке 39. Раньше такого теста не было. Если у проверяемой переменной значение true, то создание объектов, создаваемых в данной процедуре CreateObjectInfo, невозможно. Но почему мы это делаем? Чтобы понять это, нам придется немного углубиться в код.

Дальше мы находим две строки, в которых объявлены две переменные - строки 68 и 70. Изначально их не было в публикуемом коде. Но в данном случае эти переменные необходимы, одну из причин показали в строке 39: разрешить или запретить выполнение процедуры или функции. То же самое относится к строкам 165 и 194. В случае со строкой 165 есть более веская причина, которую мы рассмотрим позже. Однако в строке 194 причина та же, что и в строке 39: избежать выполнения функции или процедуры, поскольку они не будут работать или выполняться правильно, когда мы находимся в режиме перевода.

Как можно ожидалось и можно было представить, эти переменные инициализируются в конструкторе класса, это факт. Но тем временем у нас больше нет конструктора класса. Теперь у нас есть два конструктора для класса C_Mouse 😱😵😲. Если вы начинающий программист в ООП, это, вероятно, приведет вас в ужас. Однако, это совершенно нормально для ООП. Один из конструкторов написан кодом между строками 124 и 129. Другой конструктор между строками 131 и 147. Хотя у нас есть два конструктора, у нас в наличии только один деструктор.

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

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

Если внимательно присмотреться, то можно увидеть, что всё, что мы делали до сих пор, - это выделение двух кодов: одного публичного и другого защищенного. Так что если мы используем класс C_Mouse для выполнения определенного спроса, у нас будет модель использования. Однако, когда речь идет о выполнении других видов спроса, у нас будет иная модель. Но способ программирования с использованием класса всегда будет одним и тем же. Именно тогда мы используем его в нашей функции.

Посмотрите на конструктор, написанный между строками 131 и 147. Наверное вы заметили, что он практически не отличается от того, что был показан в разделе "Разработка системы репликации (Часть 31)": Проект советника — класс C_Mouse (V)". Мы сделали так специально, именно потому, что этот конструктор будет отвечать за выполнение требований класса, когда код будет использовать объекты, присутствующие в классе. Разница между этим конструктором и оригинальным заключается в строке 133, в которой инициализируется новая переменная, чтобы указать, что мы собираемся использовать класс в его первоначальном виде.

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

Это упрощает использование класса в качестве переводчика. Возможно, вы не поняли, как и зачем это делать. Но мы будем использовать класс C_Mouse в качестве переводчика указателя мыши. Наверное это кажется довольно сложным и трудным делом, не так ли? Но это совсем не сложно. Для всего этого есть причина.

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

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

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

Тогда вы поймете, насколько легче вам будет справляться с поставленными задачами. Но я всё равно хочу, чтобы вы пользовались системой перевода. Чтобы понять, как работает система перевода, достаточно взглянуть на процедуру GetInfoMouse, она находится в строке 163. Сначала это была постоянная функция. Но теперь нам нужно, чтобы он перестала быть такой. Хотя данные не могут быть изменены вне функции, нам нужно, чтобы они изменялись внутри.

В предыдущей статье Разработка системы репликации (Часть 40): Начало второй фазы (I)", вы увидите, что интерпретировать, а точнее, поддерживать стандарт написания кода для интерпретации данных индикаторного буфера очень сложно. Именно поэтому я покажу вам, как изменить класс C_Mouse, чтобы создать данную стандартизацию. Чтобы представить себе, насколько это сложно, подумайте, что каждый раз, когда нам нужно использовать указатель мыши, нам придется написать то же самое, что мы видим в функциях CheckClick и GetInfoMouse. Если коротко: Очень счастливая неприятность.

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

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

Чтобы понять ответ, который дает CheckClick, мы должны проследить и понять, как работает функция GetInfoMouse. Данную функцию можно увидеть, начиная со строки 163 и далее.

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

В строке 171 мы сбрасываем данные структуры возврата; она на самом деле является частной глобальной переменной класса, которая объявлена в строке 58. После этого мы попросим MetaTrader 5, чтобы он прочитал буфер индикатора. Если чтение из строки 172 выполняется правильно, мы начнем преобразовывать данные в возвращаемую структуру.

Перевод данных происходит точно по той же логике, что и их написание. Эту кодировку можно увидеть, рассмотрев функцию SetBuffer в предыдущей статье. Чтобы упростить ситуацию, можно увидеть ту же самую функцию в приведенном ниже отрывке:

102. inline void SetBuffer(void)
103. {
104.    uCast_Double Info;
105.    
106.    m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
107.    m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
108.    Info._datetime = (*Study).GetInfoMouse().Position.dt;
109.    m_Buff[m_posBuff + 1] = Info.dValue;
110.    Info._int[0] = (*Study).GetInfoMouse().Position.X;
111.    Info._int[1] = (*Study).GetInfoMouse().Position.Y;
112.    m_Buff[m_posBuff + 2] = Info.dValue;
113.    Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
114.    m_Buff[m_posBuff + 3] = Info.dValue;
115. }

Фрагмент кода индикатора

Обратите внимание, что информация в строке 107 фрагмента индикатора, декодируется в строке 174 класса. Чаще всего перевод осуществляется очень просто, но вы должны придерживаться правил, установленных во время кодировки. Это можно лучше понять, если сохранить данные в строке 112 фрагмента индикатора. Обратите внимание, что в данном случае мы работаем с двумя значениями, сжатыми в значение double. Когда дело доходит до перевода, мы должны делать всё наоборот. Это делается в строке 177 класса, где мы фиксируем значение, в то время как в строках 178 и 179 мы помещаем значения в нужное место для последующего использования.

То же самое происходит в строке 113 фрагмента индикатора, где мы сохраняем значение щелчка мыши. И мы переводим его в строке 181 класса. Но давайте теперь посмотрим еще раз, на строку 113 фрагмента индикатора. Обратите внимание, что тернарный оператор сохранит значение ноль, если мы находимся в режиме исследования. Важно, чтобы вы это поняли, так как если мы проводим исследование через указатель, а класс используется для его перевода, то при проверке нажатия через функцию CheckClick она вернет false, если мы проводим исследование. Подобные вещи будут происходить всегда, если, конечно, мы используем индикатор и класс в качестве переводчика.

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

Она появляется, когда мы используем класс в качестве переводчика. Однако у нас нет доступа к индикаторному буферу. Обычно это происходит, когда индикатор удаляется с графика. Когда это произойдет, строка 169 сгенерирует обработчик null, поэтому у нас не будет буфера для чтения. Нам всё равно придется запустить строку 171, которая восстановит данные.

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


Использование класса C_Mouse в качестве переводчика

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

Давайте рассмотрим код советника, который будет работать так же, как и в статье "Разработка системы репликации (Часть 31): Проект советника — класс C_Mouse (V)", но мы напишем его код по-другому.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Generic EA for use on Demo account, replay system/simulator and Real account."
04. #property description "This system has means of sending orders using the mouse and keyboard combination."
05. #property description "For more information see the article about the system."
06. #property version   "1.41"
07. #property icon "/Images/Market Replay/Icons/Replay - EA.ico"
08. #property link "https://www.mql5.com/en/articles/11607"
09. //+------------------------------------------------------------------+
10. #include <Market Replay\Auxiliar\C_Mouse.mqh>
11. //+------------------------------------------------------------------+
12. C_Mouse *mouse = NULL;
13. //+------------------------------------------------------------------+
14. int OnInit()
15. {
16.     mouse = new C_Mouse("Indicator Mouse Study");
17.     
18.     return INIT_SUCCEEDED;
19. }
20. //+------------------------------------------------------------------+
21. void OnTick() 
22. { }
23. //+------------------------------------------------------------------+
24. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
25. {
26.     C_Mouse::st_Mouse Infos;
27.     
28.     switch (id)
29.     {
30.             case CHARTEVENT_MOUSE_MOVE:
31.                     Infos = (*mouse).GetInfoMouse();
32.                     Comment((string)Infos.Position.Price + " :: [" + TimeToString(Infos.Position.dt), "]");
33.                     break;
34.     }
35. }
36. //+------------------------------------------------------------------+
37. void OnDeinit(const int reason)
38. {
39.     delete mouse;
40. }
41. //+------------------------------------------------------------------+

Код советника

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

Вы, наверное, понимаете, что на самом деле мы не делаем ничего сверхсложного, мы просто предполагаем, что всё указанное, будет на графике. В строке 16 мы сообщаем ему короткое имя индикатора, а в строке 39 удаляем класс C_Mouse.

Теперь я хочу, чтобы вы посмотрели на строки 31 и 32, которые укажут мышке данные о графике, чтобы мы могли на них посмотреть.

Самое приятное в этом то, что если мы объявим в строке 16, что используем класс в качестве индикатора, нам достаточно будет добавить вызов функции DispatchMessage в функции OnChatEvent, чтобы получить тот же эффект, что и при использовании графического индикатора. Другими словами, программа не изменится, она будет адаптироваться к тому, что нам нужно, и мы будем ее использовать.

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


Заключение

Самое важное, чтобы вы понимали, как всё происходит. Иначе вы окончательно запутаетесь в следующих статьях, где мы уже не будем действовать, как раньше. Мы будем делать всё в гораздо более сложной форме. Хотя то, что было показано в последних двух статьях, кажется сложным, всё это предназначено для тех, кто не обладает большими знаниями в области программирования. Всё, что мы рассмотрели в последних двух статьях, - это лишь небольшая подготовка к добавлению приложения Chart Trader. В следующих статьях мы начнем добавлять и развивать Chart Trader в нашей системе. Это приложение используется именно для того, чтобы мы могли торговать непосредственно на рынке, что очень полезно и важно, особенно с учетом того, что мы не можем полагаться на систему рыночных ордеров в MetaTrader 5. Поэтому нам придется создавать свои собственные, а это именно Chart Trader.

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


Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/11607

Прикрепленные файлы |
Anexo.zip (420.65 KB)
Нейросети это просто (Часть 96): Многоуровневое извлечение признаков (MSFformer) Нейросети это просто (Часть 96): Многоуровневое извлечение признаков (MSFformer)
Эффективное извлечение и объединение долгосрочных зависимостей и краткосрочных характеристик остаются важной задачей в анализе временных рядов. Правильное их понимание и интеграция необходимы для создания точных и надежных предсказательных моделей.
Как разработать агент обучения с подкреплением на MQL5 с интеграцией RestAPI (Часть 4): Организация функций в классах в MQL5 Как разработать агент обучения с подкреплением на MQL5 с интеграцией RestAPI (Часть 4): Организация функций в классах в MQL5
В данной статье рассматривается переход от процедурного написания кода к объектно-ориентированному программированию (ООП) в MQL5 с упором на интеграцию с REST API. Сегодня мы обсуждаем организацию функций HTTP-запросов (GET и POST) в классы и подчеркнем такие преимущества, как инкапсуляция, модульность и простота обслуживания. Подробно рассмотрим рефакторинг кода и покажем замену изолированных функций методами класса. Статья содержит практические примеры и тесты.
Введение в MQL5 (Часть 4): Структуры, классы и функции времени Введение в MQL5 (Часть 4): Структуры, классы и функции времени
В этой серии мы продолжаем раскрывать секреты программирования. В новой статье мы изучим в основы структур, классов и временных функций и получим новые навыки для эффективного программирования. Это руководство, возможно, будет полезно не только для новичков, но и для опытных разработчиков, поскольку упрощает сложные концепции, предоставляя ценную информацию для освоения MQL5. Продолжайте изучать новое, совершенствуйте навыки программирования и освойте мир алгоритмического трейдинга.
Нейросети это просто (Часть 95): Снижение потребления памяти в моделях Transformer Нейросети это просто (Часть 95): Снижение потребления памяти в моделях Transformer
Модели на основе архитектуры Transformer демонстрируют высокую эффективность, однако их использование осложняется большими затратами ресурсов как на этапе обучения, так и в процессе эксплуатации. В этой статье я предлагаю познакомиться с алгоритмами, которые позволяют уменьшить использование памяти такими моделями.