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

Разработка системы репликации (Часть 54): Появление первого модуля

MetaTrader 5Примеры |
433 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье "Разработка системы репликации (Часть 53): Все усложняется (V)", я объяснил некоторые концепции, которые с этого момента станут частью нашего с вами программирования, и которые будем использовать в MQL5 и платформе MetaTrader 5. Я понимаю, что многие из этих понятий новые для большинства читателей, и также знаю, что все, у кого есть опыт системного программирования (например, те, кто программирует системы Windows) знакомы с этими понятиями.

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

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


Реализуем возможности

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

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

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

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

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

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

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

Начальное содержимое данного файла показано ниже. Его надо сохранять под именем Defines.mqh. Место его расположения, можно будет увидеть в ближайшее время. Так что, если вы совершенно ничего не знаете о программировании, извините, но с этого момента вы не сможете следить за тем, что я буду реализовывать. С этой минуты, перед вами стоит барьер, препятствующий продолжению. Если вы действительно хотите использовать то, что будет рассмотрено сегодня, вам придется получить какой-то минимальный набор знаний о программирования.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_VERSION_DEBUG
05. //+------------------------------------------------------------------+
06. #ifdef def_VERSION_DEBUG
07.     #define macro_DEBUG_MODE(A) \
08.                             Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A));
09. #else
10.     #define macro_DEBUG_MODE(A)
11. #endif
12. //+------------------------------------------------------------------+
13. #define def_SymbolReplay            "RePlay"
14. #define def_MaxPosSlider            400
15. //+------------------------------------------------------------------+
16. union uCast_Double
17. {
18.     double   dValue;
19.     long     _long;                                  // 1 Information
20.     datetime _datetime;                              // 1 Information
21.     int      _int[sizeof(double) / sizeof(int)];     // 2 Informations
22.     char     _char[sizeof(double) / sizeof(char)];   // 8 Informations
23. };
24. //+------------------------------------------------------------------+
25. enum EnumEvents    {
26.                     evHideMouse,        //Hide mouse price line
27.                     evShowMouse,        //Show mouse price line
28.                     evHideBarTime,      //Hide bar time
29.                     evShowBarTime,      //Show bar time
30.                     evHideDailyVar,     //Hide daily variation
31.                     evShowDailyVar,     //Show daily variation
32.                     evHidePriceVar,     //Hide instantaneous variation
33.                     evShowPriceVar,     //Show instantaneous variation
34.                     evSetServerTime,    //Replay/simulation system timer
35.                     evCtrlReplayInit    //Initialize replay control
36.                    };
37. //+------------------------------------------------------------------+

Исходный код файла Defines.mqh

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

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

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

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

Обратите внимание, что в каждой строке мы определяем значение и даем краткий комментарий к соответствующему событию.

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

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

Как вы уже знаете, было необходимо снова изменить код классов индикатора мыши, но эти изменения необходимы для того, чтобы способствовать правильному использованию и реализации того, что мы будем делать. Прежде чем изменить код индикатора мыши, необходимо внести небольшое изменение в код класса C_Terminal. Поэтому в заголовочном файле C_Terminal.mqh нужно сделать так, как показано в следующем фрагменте.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Macros.mqh"
05. #include "..\Defines.mqh"
06. #include "Interprocess.mqh"
07. //+------------------------------------------------------------------+
08. class C_Terminal
09. {
10. //+------------------------------------------------------------------+
11.     protected:
12.             enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance};
13. //+------------------------------------------------------------------+
14.             struct st_Terminal

Фрагмент кода из файла C_Terminal.mqh

Посмотрите внимательно на строку 5, в которой указано расположение заголовочного файла Defines.mqh, упомянутого выше, по отношению к заголовочному файлу C_Terminal.mqh. Но это не всё, что я хочу подчеркнуть. Обратите внимание, что строка 6 была удалена из кода, т.е. теперь можно удалить заголовочный файл InterProcess.mqh из проекта, так как он больше не будет использоваться.

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

Обратите внимание, что в строке 12 есть перечисление, которому было присвоено новое значение. Оно должно использоваться во время теста, чтобы проверить, не встречается ли уже на графике такой же индикатор. Поэтому необходимо внести еще одно маленькое изменение в тот же заголовочный файл C_Terminal.mqh. Изменение показано в следующем фрагменте.

157. //+------------------------------------------------------------------+
158.            bool IndicatorCheckPass(const string szShortName)
159.                    {
160.                            string szTmp = szShortName + "_TMP";
161.                            
162.                            if (_LastError != ERR_SUCCESS) return false;
163.                            IndicatorSetString(INDICATOR_SHORTNAME, szTmp);
164.                            if (ChartWindowFind(m_Infos.ID, szShortName) != -1)
165.                            {
166.                                    ChartIndicatorDelete(m_Infos.ID, 0, szTmp);
167.                                    Print("Only one instance is allowed...");
168.                                    SetUserError(C_Terminal::ERR_NoMoreInstance);
169.                                    
170.                                    return false;
171.                            }
172.                            IndicatorSetString(INDICATOR_SHORTNAME, szShortName);
173.                            ResetLastError();
174.    
175.                            return true;
176.                    }
177. //+------------------------------------------------------------------+

Фрагмент, который необходимо изменить в файле C_Terminal.mqh

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

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

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

Исходный код файла C_Mouse.mqh

В отличие от предыдущих версий, теперь считается только необходимым файл C_Terminal.mqh. Однако вам стоит обратить внимание на другие детали, которые были изменены в этом классе, как показано выше.

Если вы заметили, теперь в строке 30 у нас появилась новая переменная. Она полезна только в том случае, если используется сервис репликации/моделирования. Данная переменная дает нам доступ к значению, предоставляемому сервисом репликации/моделирования, которое ранее было получено через глобальную переменную терминала. Хотя эта переменная объявлена в строке 30, она используется не здесь, а в другом месте. Но из-за другой проблемы, которую мы разъясним позже, пришлось добавить ее сюда, в класс C_Mouse.

Как видите, большая часть кода остается идентичной предыдущей, до строки 168, где мы находим кое-что другое. Теперь мы будем использовать шесть буферных позиций. Шестую позицию должна занять переменная, объявленная в строке 30. Итак, в строке 181 мы заполняем значение, когда нам нужно прочитать его и узнать, какое значение у этой переменной. Здесь я должен остановиться на секунду и кое-что объяснить. Единственной процедурой, которая будет использовать содержимое переменной, объявленной в строке 30, будет сервис репликации/моделирования и индикатор мыши. Именно он должен использовать данное значение для того, чтобы сообщить пользователю о времени, оставшемся до открытия следующего бара. Однако, если мы не хотим его использовать, можно удалить его с индикатора мыши или сервиса репликации/моделирования. Проблем не возникнет, если вы сделаете это. Но при желании использовать эту информацию, вы должны знать, что если мы не используем глобальную переменную терминала или другие средства для хранения значения времени в сервисе репликации/моделирования, данный сервис не будет знать, когда эти данные должны быть обновлены снова.

По этой причине мы помещаем значение в буфер индикатора мыши. Но тогда вы себя спросите: "а разве это значение не будет удалено и обнулено, когда MetaTrader 5 вернет индикатор на график, ведь пользователь изменил таймфрейм графика?". Тогда хранить его в буфере индикатора не имеет никакого смысла. На самом деле, это именно то, чего мы хотим. Когда сервис обнаружит, что значение, находящееся в буфере, уже не соответствует действительности, он отправит событие на индикатор мыши, чтобы снова обновить это значение. Таким образом, мы сохраним всё, как полагается.

То есть тот факт, что MetaTrader 5 заставляет индикатор сбрасывать буфер, заставляет сервис репликации/моделирования понять, что пользователь внес какое-то изменение, которое должно быть повторно оценено сервисом репликации/моделирования. Таким образом, наше приложение без труда обнаружит: что-то произошло, а MetaTrader 5 сделает всё за нас.

Но вопрос становится еще более интересным, если мы посмотрим на код, начиная со строки 189, где как раз и находится обработчик сообщений класса C_Mouse.

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

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

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

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

Третье событие, которое также будем реализовать здесь, показано в строке 205. В этом случае мы указываем значение, которое надо помещать как время работы сервиса, т.е. именно сервис репликации/моделирования будет делать это на самом деле. Ведь только он получит реальную выгоду от проведения такого мероприятия. Но здесь есть один важный момент: когда это событие срабатывает, оно должно указывать значение времени. Данное значение должно находиться в параметре dparam, который представляет собой двойное значение.

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

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

Хорошо, первая часть работы выполнена. Но мы всё равно будем улучшать ситуацию. Если вы действительно были внимательны, то наверняка заметили, что есть и другие события, связанные с индикатором мыши. Только данные события находятся не в заголовочном файле C_Mouse.mqh, а в файле C_Study.mqh. Чтобы увидеть эти события и понять, что произойдет, давайте посмотрим на этот код. Полностью его можно увидеть ниже:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\C_Mouse.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ExpansionPrefix def_MousePrefixName + "Expansion_"
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.                    bool           bvT, bvD, bvP;
024.            }m_Info;
025. //+------------------------------------------------------------------+
026.            const datetime GetBarTime(void)
027.                    {
028.                            datetime dt;
029.                            int i0 = PeriodSeconds();
030.                            
031.                            if (m_Info.Status == eInReplay)
032.                            {
033.                                    if ((dt = GetInfoMouse().TimeDevice) == ULONG_MAX) return ULONG_MAX;
034.                            }else dt = TimeCurrent();
035.                            if (m_Info.Rate.time <= dt)
036.                                    m_Info.Rate.time = (datetime)(((ulong) dt / i0) * i0) + i0;
037. 
038.                            return m_Info.Rate.time - dt;
039.                    }
040. //+------------------------------------------------------------------+
041.            void Draw(void)
042.                    {
043.                            double v1;
044.                            
045.                            if (m_Info.bvT)
046.                            {
047.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 18);
048.                                    ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_TEXT, m_Info.szInfo);
049.                            }
050.                            if (m_Info.bvD)
051.                            {
052.                                    v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / GetInfoMouse().Position.Price) * 100.0), 2);
053.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1);
054.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
055.                                    ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
056.                            }
057.                            if (m_Info.bvP)
058.                            {
059.                                    v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0)) * 100.0), 2);
060.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1);
061.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
062.                                    ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
063.                            }
064.                    }
065. //+------------------------------------------------------------------+
066. inline void CreateObjInfo(EnumEvents arg)
067.                    {
068.                            switch (arg)
069.                            {
070.                                    case evShowBarTime:
071.                                            C_Mouse::CreateObjToStudy(2, 110, def_ExpansionBtn1, clrPaleTurquoise);
072.                                            m_Info.bvT = true;
073.                                            break;
074.                                    case evShowDailyVar:
075.                                            C_Mouse::CreateObjToStudy(2, 53, def_ExpansionBtn2);
076.                                            m_Info.bvD = true;
077.                                            break;
078.                                    case evShowPriceVar:
079.                                            C_Mouse::CreateObjToStudy(58, 53, def_ExpansionBtn3);
080.                                            m_Info.bvP = true;
081.                                            break;
082.                            }
083.                    }
084. //+------------------------------------------------------------------+
085. inline void RemoveObjInfo(EnumEvents arg)
086.                    {
087.                            string sz;
088.                            
089.                            switch (arg)
090.                            {
091.                                    case evHideBarTime:
092.                                            sz = def_ExpansionBtn1;
093.                                            m_Info.bvT = false;
094.                                            break;
095.                                    case evHideDailyVar:
096.                                            sz = def_ExpansionBtn2;
097.                                            m_Info.bvD      = false;
098.                                            break;
099.                                    case evHidePriceVar:
100.                                            sz = def_ExpansionBtn3;
101.                                            m_Info.bvP = false;
102.                                            break;
103.                            }
104.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
105.                            ObjectDelete(GetInfoTerminal().ID, sz);
106.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
107.                    }
108. //+------------------------------------------------------------------+
109.    public  :
110. //+------------------------------------------------------------------+
111.            C_Study(long IdParam, string szShortName, color corH, color corP, color corN)
112.                    :C_Mouse(IdParam, szShortName, corH, corP, corN)
113.                    {
114.                            if (_LastError != ERR_SUCCESS) return;
115.                            ZeroMemory(m_Info);
116.                            m_Info.Status = eCloseMarket;
117.                            m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1));
118.                            m_Info.corP = corP;
119.                            m_Info.corN = corN;
120.                            CreateObjInfo(evShowBarTime);
121.                            CreateObjInfo(evShowDailyVar);
122.                            CreateObjInfo(evShowPriceVar);
123.                    }
124. //+------------------------------------------------------------------+
125.            void Update(const eStatusMarket arg)
126.                    {
127.                            datetime dt;
128.                            
129.                            switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
130.                            {
131.                                    case eCloseMarket :
132.                                            m_Info.szInfo = "Closed Market";
133.                                            break;
134.                                    case eInReplay    :
135.                                    case eInTrading   :
136.                                            if ((dt = GetBarTime()) < ULONG_MAX)
137.                                            {
138.                                                    m_Info.szInfo = TimeToString(dt, TIME_SECONDS);
139.                                                    break;
140.                                            }
141.                                    case eAuction     :
142.                                            m_Info.szInfo = "Auction";
143.                                            break;
144.                                    default           :
145.                                            m_Info.szInfo = "ERROR";
146.                            }
147.                            Draw();
148.                    }
149. //+------------------------------------------------------------------+
150. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
151.                    {
152.                            C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
153.                            switch (id)
154.                            {
155.                                    case CHARTEVENT_CUSTOM + evHideBarTime:
156.                                            RemoveObjInfo(evHideBarTime);
157.                                            break;
158.                                    case CHARTEVENT_CUSTOM + evShowBarTime:
159.                                            CreateObjInfo(evShowBarTime);
160.                                            break;
161.                                    case CHARTEVENT_CUSTOM + evHideDailyVar:
162.                                            RemoveObjInfo(evHideDailyVar);
163.                                            break;
164.                                    case CHARTEVENT_CUSTOM + evShowDailyVar:
165.                                            CreateObjInfo(evShowDailyVar);
166.                                            break;
167.                                    case CHARTEVENT_CUSTOM + evHidePriceVar:
168.                                            RemoveObjInfo(evHidePriceVar);
169.                                            break;
170.                                    case CHARTEVENT_CUSTOM + evShowPriceVar:
171.                                            CreateObjInfo(evShowPriceVar);
172.                                            break;
173.                                    case CHARTEVENT_MOUSE_MOVE:
174.                                            Draw();
175.                                            break;
176.                            }
177.                            ChartRedraw(GetInfoTerminal().ID);
178.                    }
179. //+------------------------------------------------------------------+
180. };
181. //+------------------------------------------------------------------+
182. #undef def_ExpansionBtn3
183. #undef def_ExpansionBtn2
184. #undef def_ExpansionBtn1
185. #undef def_ExpansionPrefix
186. #undef def_MousePrefixName
187. //+------------------------------------------------------------------+

Исходный код файла C_Study.mqh

В отличие от C_Mouse.mqh, здесь есть много отличий. Начнем с того, что у нас появились новые переменные, проверки и другие вещи, которые сейчас реализуются. Но поскольку большинство из них просты, мы можем остановиться лишь на нескольких более «необычных». Среди них строка 33, где мы видим, что функция для доступа к глобальной переменной на терминале больше не используется. Теперь мы запрашиваем у индикатора мыши то, что раньше искали в глобальной переменной терминала. Именно так мы хотим действовать, то есть мы хотим делать вещи без необходимости использовать что-то такое, чем может управлять пользователь, но без необходимости прибегать к внешнему программированию. Идея в том, чтобы всё реализовать на чистом MQL5.

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

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

Большая часть кода, которую можно увидеть в файле C_Study.mqh, делает именно это. Но сейчас нас действительно интересует то, что происходит, начиная со строки 150 и далее, где мы будем реализовывать описанное в этом заголовочном файле. Обратите внимание, что те части, которые нам неинтересно рассматривать, передаются в класс C_Mouse, чтобы там можно было обрабатывать события. Это видно из строки 152.

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

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

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

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. #property description "This is an indicator for graphical studies using the mouse."
004. #property description "This is an integral part of the Replay / Simulator system."
005. #property description "However it can be used in the real market."
006. #property version "1.54"
007. #property icon "/Images/Market Replay/Icons/Indicators.ico"
008. #property link "https://www.mql5.com/pt/articles/11971"
009. #property indicator_chart_window
010. #property indicator_plots 0
011. #property indicator_buffers 1
012. //+------------------------------------------------------------------+
013. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
014. //+------------------------------------------------------------------+
015. C_Study *Study     = NULL;
016. //+------------------------------------------------------------------+
017. input long user00  = 0;                                    //ID
018. input C_Study::eStatusMarket user01 = C_Study::eAuction;   //Market Status
019. input color user02 = clrBlack;                             //Price Line
020. input color user03 = clrPaleGreen;                         //Positive Study
021. input color user04 = clrLightCoral;                        //Negative Study
022. //+------------------------------------------------------------------+
023. C_Study::eStatusMarket m_Status;
024. int m_posBuff = 0;
025. double m_Buff[];
026. //+------------------------------------------------------------------+
027. int OnInit()
028. {
029.    ResetLastError();
030.    Study = new C_Study(user00, "Indicator Mouse Study", user02, user03, user04);
031.    if (_LastError != ERR_SUCCESS) return INIT_FAILED;
032.    if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
033.    {
034.            MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
035.            OnBookEvent((*Study).GetInfoTerminal().szSymbol);
036.            m_Status = C_Study::eCloseMarket;
037.    }else
038.            m_Status = user01;
039.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
040.    ArrayInitialize(m_Buff, EMPTY_VALUE);
041.    
042.    return INIT_SUCCEEDED;
043. }
044. //+------------------------------------------------------------------+
045. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
046. {
047.    m_posBuff = rates_total - 6;
048.    (*Study).Update(m_Status);      
049.    
050.    return rates_total;
051. }
052. //+------------------------------------------------------------------+
053. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
054. {
055.    (*Study).DispatchMessage(id, lparam, dparam, sparam);
056.    SetBuffer();
057.    
058.    ChartRedraw((*Study).GetInfoTerminal().ID);
059. }
060. //+------------------------------------------------------------------+
061. void OnBookEvent(const string &symbol)
062. {
063.    MqlBookInfo book[];
064.    C_Study::eStatusMarket loc = m_Status;
065.    
066.    if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
067.    MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
068.    m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading);
069.    for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
070.            if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
071.    if (loc != m_Status) (*Study).Update(m_Status);
072. }
073. //+------------------------------------------------------------------+
074. void OnDeinit(const int reason)
075. {
076.    if (reason != REASON_INITFAILED)
077.    {
078.            if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
079.                    MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
080.    }
081.    delete Study;
082. }
083. //+------------------------------------------------------------------+
084. inline void SetBuffer(void)
085. {
086.    uCast_Double Info;
087.    
088.    m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
089.    m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
090.    Info._datetime = (*Study).GetInfoMouse().Position.dt;
091.    m_Buff[m_posBuff + 1] = Info.dValue;
092.    Info._int[0] = (*Study).GetInfoMouse().Position.X_Adjusted;
093.    Info._int[1] = (*Study).GetInfoMouse().Position.Y_Adjusted;
094.    m_Buff[m_posBuff + 2] = Info.dValue;
095.    Info._int[0] = (*Study).GetInfoMouse().Position.X_Graphics;
096.    Info._int[1] = (*Study).GetInfoMouse().Position.Y_Graphics;
097.    m_Buff[m_posBuff + 3] = Info.dValue;
098.    Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
099.    m_Buff[m_posBuff + 4] = Info.dValue;
100.    m_Buff[m_posBuff + 5] = (double)(*Study).GetInfoMouse().TimeDevice;
101. }
102. //+------------------------------------------------------------------+

Исходный код индикатора мыши

Стоит отметить, что код не претерпел существенных изменений. Есть только одно новое дополнение, которое можно увидеть в строке 100, где данные помещаются в буфер индикатора. Опять же, эти данные полезны только на ранней стадии для сервиса репликации/моделирования. У него нет другого применения. По крайней мере, я не заметил ни одного другого.


Заключение

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

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

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

Именно это я и хотел сделать, поскольку на собственном опыте убедился, что в предыдущих статьях люди пытались скомпилировать что-то, не понимая, о чем идет речь. Такие вещи не только рискованны но и опасны. Не стоит использовать вещи без понимания их назначения.


Видео 01 - Демонстрация работы модуля

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

Прикрепленные файлы |
Anexo.zip (420.65 KB)
Нейросети в трейдинге: Адаптивное представление графов (NAFS) Нейросети в трейдинге: Адаптивное представление графов (NAFS)
Предлагаем познакомиться с методом NAFS (Node-Adaptive Feature Smoothing) — это непараметрический подход к созданию представлений узлов, который не требует обучения параметров. NAFS извлекает характеристики каждого узла, учитывая его соседей, и затем адаптивно комбинирует эти характеристики для формирования конечного представления.
Методы оптимизации библиотеки ALGLIB (Часть I) Методы оптимизации библиотеки ALGLIB (Часть I)
В статье познакомимся с методами оптимизации библиотеки ALGLIB для MQL5. Статья включает простые и наглядные примеры применения ALGLIB для решения задач оптимизации, что сделает процесс освоения методов максимально доступным. Мы подробно рассмотрим подключение таких алгоритмов, как BLEIC, L-BFGS и NS, и на их основе решим простую тестовую задачу.
Возможности Мастера MQL5, которые вам нужно знать (Часть 22): Условные генеративно-состязательные сети (cGAN) Возможности Мастера MQL5, которые вам нужно знать (Часть 22): Условные генеративно-состязательные сети (cGAN)
Генеративно-состязательные сети — это пара нейронных сетей, которые обучаются друг на друге для получения более точных результатов. Мы рассмотрим условный тип этих сетей в контексте их возможного применения в прогнозировании финансовых временных рядов в рамках класса сигналов советника.
Упрощаем торговлю на новостях (Часть 2): Управляем рисками Упрощаем торговлю на новостях (Часть 2): Управляем рисками
В этой статье мы добавим наследование в предыдущий и новый код. Для обеспечения эффективности будет внедрена новая структура базы данных. Кроме того, мы создадим класс по управлению рисками для расчета объемов.