Español Português
preview
Разработка системы репликации (Часть 47): Проект Chart Trade (VI)

Разработка системы репликации (Часть 47): Проект Chart Trade (VI)

MetaTrader 5Примеры | 23 сентября 2024, 12:12
189 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье Разработка системы репликации (Часть 46): Проект Chart Trade (V) я показал, как можно добавить данные в исполняемый файл, чтобы их не нужно было отдельно переносить. Эти знания очень важны для того, что будет сделано в ближайшее время. Но пока мы продолжим разрабатывать то, что необходимо реализовать в первую очередь.

В этой статье мы доработаем индикатор Chart Trade, сделав его функциональным настолько, что его можно будет использовать вместе с некоторыми советниками. Мы сделаем это таким образом, чтобы иметь доступ к индикатору и работать с ним так, как если бы он действительно был связан с советником. Но сделаем это гораздо интереснее, чем в предыдущей статье: Разработка торгового советника с нуля (часть 30): CHART TRADE теперь как индикатор?!. В той статье мы используем Chart Trade в качестве условного индикатора, здесь же он будет самым настоящим индикатором. 

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

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

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


Небольшие изменения, большие результаты

Изменения, которые будут сделаны, немногочисленны и несложны. Это для тех, кто следит за этой серией статей. Отмечу, что мы находимся на втором этапе статей о системе репликации/моделирования. Чуть ниже у вас есть доступ к полному коду индикатора Chart Trade. Важно сначала просмотреть этот код, чтобы упростить объяснение кода класса.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Chart Trade base indicator."
04. #property description "This version communicates via buffer with the EA."
05. #property description "See the articles for more details."
06. #property version   "1.47"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/es/articles/11760"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. #property indicator_buffers 1
12. //+------------------------------------------------------------------+
13. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
14. //+------------------------------------------------------------------+
15. C_ChartFloatingRAD *chart = NULL;
16. //+------------------------------------------------------------------+
17. input int      user01 = 1;       //Leverage
18. input double   user02 = 100.1;   //Finance Take
19. input double   user03 = 75.4;    //Finance Stop
20. //+------------------------------------------------------------------+
21. double m_Buff[];
22. //+------------------------------------------------------------------+
23. int OnInit()
24. {
25.     bool bErr;
26.             
27.     chart = new C_ChartFloatingRAD("Indicator Chart Trade", new C_Mouse("Indicator Mouse Study"), user01, user02, user03);
28.     
29.     if (bErr = (_LastError != ERR_SUCCESS)) Print(__FILE__, " - [Error]: ", _LastError);
30. 
31.     SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
32.     ArrayInitialize(m_Buff, EMPTY_VALUE);
33.     
34.     return (bErr ? INIT_FAILED : INIT_SUCCEEDED);
35. }
36. //+------------------------------------------------------------------+
37. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
38. {
39.     (*chart).MountBuffer(m_Buff, rates_total);
40.     
41.     return rates_total;
42. }
43. //+------------------------------------------------------------------+
44. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
45. {
46.     (*chart).DispatchMessage(id, lparam, dparam, sparam);
47.     (*chart).MountBuffer(m_Buff);
48.     
49.     ChartRedraw();
50. }
51. //+------------------------------------------------------------------+
52. void OnDeinit(const int reason)
53. {
54.     if (reason == REASON_CHARTCHANGE) (*chart).SaveState();
55. 
56.     delete chart;
57. }
58. //+------------------------------------------------------------------+

Исходный код индикатора Chart Trade

Обратите внимание, что весь приведенный выше код содержит все необходимое для функционирования индикатора Chart Trade. Но из практических соображений большая часть кода была перенесена в класс, который будет рассмотрен позже. Тем не менее, возможно вы задумались: как работает этот код? Как он позволяет нам отправлять команды советнику на выполнение операций? Подождите, давайте сначала разберемся, что происходит здесь, в коде индикатора.

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

Буфер, который мы будем использовать, объявляется в строке 21. Но мы определяем, как его использовать, только в строке 31. Поскольку мы не хотим, чтобы буфер заполнялся «мусором», мы инициируем его в строке 32 так, чтобы он содержал исключительно нулевые значения.

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

001.//+------------------------------------------------------------------+
002.#property copyright "Daniel Jose"
003.//+------------------------------------------------------------------+
004.#include "../Auxiliar/C_Mouse.mqh"
005.#include "../Auxiliar/Interprocess.mqh"
006.#include "C_AdjustTemplate.mqh"
007.//+------------------------------------------------------------------+
008.#define macro_NameGlobalVariable(A) StringFormat("ChartTrade_%u%s", GetInfoTerminal().ID, A)
009.//+------------------------------------------------------------------+
010.class C_ChartFloatingRAD : private C_Terminal
011.{
012.    public          :
013.            enum eObjectsIDE {MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL};
014.            struct stData
015.            {
016.                    int             Leverage;
017.                    double          PointsTake,
018.                                    PointsStop;
019.                    bool            IsDayTrade;
020.                    union u01
021.                    {
022.                            ulong   TickCount;
023.                            double  dValue;
024.                    }uCount;
025.                    eObjectsIDE Msg;
026.            };
027.    private :
028.            struct st00
029.            {
030.                    int     x, y, minx, miny;
031.                    string  szObj_Chart,
032.                            szObj_Editable,
033.                            szFileNameTemplate;
034.                    long    WinHandle;
035.                    double  FinanceTake,
036.                            FinanceStop;
037.                    bool    IsMaximized;
038.                    stData  ConfigChartTrade;
039.                    struct st01
040.                    {
041.                            int    x, y, w, h;
042.                            color  bgcolor;
043.                            int    FontSize;
044.                            string FontName;
045.                    }Regions[MSG_NULL];
046.            }m_Info;
047.//+------------------------------------------------------------------+
048.            C_Mouse   *m_Mouse;
049.            string    m_szShortName;
050.//+------------------------------------------------------------------+
051.            void CreateWindowRAD(int w, int h)
052.                    {
053.                            m_Info.szObj_Chart = "Chart Trade IDE";
054.                            m_Info.szObj_Editable = m_Info.szObj_Chart + " > Edit";
055.                            ObjectCreate(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0);
056.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x);
057.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y);
058.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w);
059.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h);
060.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false);
061.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false);
062.                            m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_CHART_ID);
063.                    };
064.//+------------------------------------------------------------------+
065.            void AdjustEditabled(C_AdjustTemplate &Template, bool bArg)
066.                    {
067.                            for (eObjectsIDE c0 = 0; c0 <= MSG_STOP_VALUE; c0++)
068.                                    if (bArg)
069.                                    {
070.                                            Template.Add(EnumToString(c0), "bgcolor", NULL);
071.                                            Template.Add(EnumToString(c0), "fontsz", NULL);
072.                                            Template.Add(EnumToString(c0), "fontnm", NULL);
073.                                    }
074.                                    else
075.                                    {
076.                                            m_Info.Regions[c0].bgcolor = (color) StringToInteger(Template.Get(EnumToString(c0), "bgcolor"));
077.                                            m_Info.Regions[c0].FontSize = (int) StringToInteger(Template.Get(EnumToString(c0), "fontsz"));
078.                                            m_Info.Regions[c0].FontName = Template.Get(EnumToString(c0), "fontnm");
079.                                    }
080.                    }
081.//+------------------------------------------------------------------+
082.inline void AdjustTemplate(const bool bFirst = false)
083.                    {
084.#define macro_AddAdjust(A) {                         \
085.             (*Template).Add(A, "size_x", NULL);     \
086.             (*Template).Add(A, "size_y", NULL);     \
087.             (*Template).Add(A, "pos_x", NULL);      \
088.             (*Template).Add(A, "pos_y", NULL);      \
089.                           }
090.#define macro_GetAdjust(A) {                                                                                                                                                        \
091.             m_Info.Regions[A].x = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_x"));   \
092.             m_Info.Regions[A].y = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_y"));   \
093.             m_Info.Regions[A].w = (int) StringToInteger((*Template).Get(EnumToString(A), "size_x"));  \
094.             m_Info.Regions[A].h = (int) StringToInteger((*Template).Get(EnumToString(A), "size_y"));  \
095.                           }
096.#define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.ConfigChartTrade.Leverage - 1))) * GetInfoTerminal().AdjustToTrade
097.                            
098.                            C_AdjustTemplate  *Template;
099.                            
100.                            if (bFirst)
101.                            {
102.                                    Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true);
103.                                    for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_AddAdjust(EnumToString(c0));
104.                                    AdjustEditabled(Template, true);
105.                            }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate);
106.                            m_Info.ConfigChartTrade.Leverage = (m_Info.ConfigChartTrade.Leverage <= 0 ? 1 : m_Info.ConfigChartTrade.Leverage);
107.                            m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.ConfigChartTrade.Leverage));
108.                            m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.ConfigChartTrade.Leverage));
109.                            m_Info.ConfigChartTrade.PointsTake = FinanceToPoints(m_Info.FinanceTake, m_Info.ConfigChartTrade.Leverage);
110.                            m_Info.ConfigChartTrade.PointsStop = FinanceToPoints(m_Info.FinanceStop, m_Info.ConfigChartTrade.Leverage);
111.                            (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol);
112.                            (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.ConfigChartTrade.Leverage));
113.                            (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2));
114.                            (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2));
115.                            (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.ConfigChartTrade.IsDayTrade ? "1" : "0"));
116.                            (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0"));
117.                            (*Template).Execute();
118.                            if (bFirst)
119.                            {
120.                                    for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_GetAdjust(c0);
121.                                    m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x;
122.                                    AdjustEditabled(Template, false);
123.                            };
124.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? 210 : m_Info.Regions[MSG_TITLE_IDE].h + 6));
125.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx));
126.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny));
127.
128.                            delete Template;
129.                            
130.                            ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate);
131.                            ChartRedraw(m_Info.WinHandle);
132.
133.#undef macro_PointsToFinance
134.#undef macro_GetAdjust
135.#undef macro_AddAdjust
136.                    }
137.//+------------------------------------------------------------------+
138.            eObjectsIDE CheckMousePosition(const int x, const int y)
139.                    {
140.                            int xi, yi, xf, yf;
141.                            
142.                            for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++)
143.                            {
144.                                    xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x;
145.                                    yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y;
146.                                    xf = xi + m_Info.Regions[c0].w;
147.                                    yf = yi + m_Info.Regions[c0].h;
148.                                    if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0;
149.                            }
150.                            return MSG_NULL;
151.                    }
152.//+------------------------------------------------------------------+
153.inline void DeleteObjectEdit(void)
154.                    {
155.                            ChartRedraw();
156.                            ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Editable);
157.                            m_Info.ConfigChartTrade.Msg = MSG_NULL;
158.                    }
159.//+------------------------------------------------------------------+
160.            template <typename T >
161.            void CreateObjectEditable(eObjectsIDE arg, T value)
162.                    {
163.                            long id = GetInfoTerminal().ID;
164.                            
165.                            DeleteObjectEdit();
166.                            CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0);
167.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3);
168.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3);
169.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w);
170.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h);
171.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor);
172.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER);
173.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1);
174.                            ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName);
175.                            ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value));
176.                            ChartRedraw();
177.                            m_Info.ConfigChartTrade.Msg = MSG_NULL;
178.                    }
179.//+------------------------------------------------------------------+
180.            bool RestoreState(void)
181.                    {
182.                            uCast_Double info;
183.                            bool bRet;
184.                            
185.                            if (bRet = GlobalVariableGet(macro_NameGlobalVariable("P"), info.dValue))
186.                            {
187.                                    m_Info.x = info._int[0];
188.                                    m_Info.y = info._int[1];
189.                            }
190.                            if (bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("M"), info.dValue) : bRet))
191.                            {
192.                                    m_Info.minx = info._int[0];
193.                                    m_Info.miny = info._int[1];
194.                            }
195.                            if (bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("B"), info.dValue) : bRet))
196.                            {
197.                                    m_Info.ConfigChartTrade.IsDayTrade = info._char[0];
198.                                    m_Info.IsMaximized = info._char[1];
199.                                    m_Info.ConfigChartTrade.Msg = (eObjectsIDE)info._char[2];
200.                            }
201.                            if (bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("L"), info.dValue) : bRet))
202.                                    m_Info.ConfigChartTrade.Leverage = info._int[0];
203.                            bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("Y"), m_Info.ConfigChartTrade.uCount.dValue) : bRet);
204.                            bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("T"), m_Info.FinanceTake) : bRet);
205.                            bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("S"), m_Info.FinanceStop) : bRet);
206.                            
207.                            
208.                            GlobalVariablesDeleteAll(macro_NameGlobalVariable(""));
209.                            
210.                            return bRet;
211.                    }
212.//+------------------------------------------------------------------+
213.    public  :
214.//+------------------------------------------------------------------+
215.            C_ChartFloatingRAD(const string szShortName)
216.                    :m_Mouse(NULL),
217.                     m_szShortName(szShortName)
218.                    {
219.                    }
220.//+------------------------------------------------------------------+
221.            C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const int Leverage, const double FinanceTake, const double FinanceStop)
222.                    :C_Terminal(),
223.                     m_szShortName(NULL)
224.                    {
225.                            if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
226.                            m_Mouse = MousePtr;
227.                            if (!RestoreState())
228.                            {
229.                                    m_Info.ConfigChartTrade.Leverage = Leverage;
230.                                    m_Info.FinanceTake = FinanceTake;
231.                                    m_Info.FinanceStop = FinanceStop;
232.                                    m_Info.ConfigChartTrade.IsDayTrade = true;
233.                                    m_Info.ConfigChartTrade.uCount.TickCount = 0;
234.                                    m_Info.ConfigChartTrade.Msg = MSG_NULL;
235.                                    m_Info.IsMaximized = true;
236.                                    m_Info.minx = m_Info.x = 115;
237.                                    m_Info.miny = m_Info.y = 64;
238.                            }
239.                            CreateWindowRAD(170, 210);
240.                            AdjustTemplate(true);
241.                    }
242.//+------------------------------------------------------------------+
243.            ~C_ChartFloatingRAD()
244.                    {
245.                            if (m_Mouse == NULL) return;
246.                            ChartRedraw();
247.                            ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Chart);
248.                            FileDelete(m_Info.szFileNameTemplate);
249.                                                            
250.                            delete m_Mouse;
251.                    }
252.//+------------------------------------------------------------------+
253.            void SaveState(void)
254.                    {
255.#define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B);
256.                            
257.                            uCast_Double info;
258.                            
259.                            if (m_Mouse == NULL) return;
260.                            info._int[0] = m_Info.x;
261.                            info._int[1] = m_Info.y;
262.                            macro_GlobalVariable(macro_NameGlobalVariable("P"), info.dValue);
263.                            info._int[0] = m_Info.minx;
264.                            info._int[1] = m_Info.miny;
265.                            macro_GlobalVariable(macro_NameGlobalVariable("M"), info.dValue);
266.                            info._char[0] = m_Info.ConfigChartTrade.IsDayTrade;
267.                            info._char[1] = m_Info.IsMaximized;
268.                            info._char[2] = (char)m_Info.ConfigChartTrade.Msg;
269.                            macro_GlobalVariable(macro_NameGlobalVariable("B"), info.dValue);
270.                            info._int[0] = m_Info.ConfigChartTrade.Leverage;
271.                            macro_GlobalVariable(macro_NameGlobalVariable("L"), info.dValue);
272.                            macro_GlobalVariable(macro_NameGlobalVariable("T"), m_Info.FinanceTake);
273.                            macro_GlobalVariable(macro_NameGlobalVariable("S"), m_Info.FinanceStop);
274.                            macro_GlobalVariable(macro_NameGlobalVariable("Y"), m_Info.ConfigChartTrade.uCount.dValue);
275.                            
276.#undef macro_GlobalVariable
277.                    }
278.//+------------------------------------------------------------------+
279.inline void MountBuffer(double &Buff[], const int iPos = -1)
280.                    {
281.                            static int posBuff = 0;
282.                            uCast_Double info;
283.
284.                            if ((m_szShortName != NULL) || (m_Info.ConfigChartTrade.Msg == MSG_NULL)) return;
285.                            posBuff = (iPos > 5 ? iPos - 5 : posBuff);
286.                            Buff[posBuff + 0] = m_Info.ConfigChartTrade.uCount.dValue;
287.                            info._char[0] = (char)m_Info.ConfigChartTrade.IsDayTrade;
288.                            info._char[1] = (char)m_Info.ConfigChartTrade.Msg;
289.                            Buff[posBuff + 1] = info.dValue;
290.                            info._int[0] = m_Info.ConfigChartTrade.Leverage;
291.                            Buff[posBuff + 2] = info.dValue;
292.                            Buff[posBuff + 3] = m_Info.ConfigChartTrade.PointsTake;
293.                            Buff[posBuff + 4] = m_Info.ConfigChartTrade.PointsStop;
294.                    }
295.//+------------------------------------------------------------------+
296.inline const stData GetDataBuffer(void)
297.                    {
298.                            double Buff[];
299.                            int handle;
300.                            uCast_Double info;
301.                            stData data;
302.                            
303.                            ZeroMemory(data);
304.                            if (m_szShortName == NULL) return data;
305.                            if ((handle = ChartIndicatorGet(ChartID(), 0, m_szShortName)) == INVALID_HANDLE) return data;
306.                            if (CopyBuffer(handle, 0, 0, 5, Buff) == 5)
307.                            {
308.                                    data.uCount.dValue = Buff[0];
309.                                    info.dValue = Buff[1];
310.                                    data.IsDayTrade = (bool)info._char[0];
311.                                    data.Msg = (C_ChartFloatingRAD::eObjectsIDE) info._char[1];
312.                                    info.dValue = Buff[2];
313.                                    data.Leverage = info._int[0];
314.                                    data.PointsTake = Buff[3];
315.                                    data.PointsStop = Buff[4];
316.                            }
317.                            if (handle != INVALID_HANDLE) IndicatorRelease(handle);
318.                            
319.                            return data;
320.                    };
321.//+------------------------------------------------------------------+
322.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
323.                    {
324.#define macro_AdjustMinX(A, B)  {                            \
325.             B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x;  \
326.             mx = x - m_Info.Regions[MSG_TITLE_IDE].w;       \
327.             A = (B ? (mx > 0 ? mx : 0) : A);                \
328.                                }
329.#define macro_AdjustMinY(A, B)  {                            \
330.             B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y;  \
331.             my = y - m_Info.Regions[MSG_TITLE_IDE].h;       \
332.             A = (B ? (my > 0 ? my : 0) : A);                \
333.                                }
334.                                                                  
335.                            static int sx = -1, sy = -1;
336.                            int x, y, mx, my;
337.                            static eObjectsIDE obj = MSG_NULL;
338.                            double dvalue;
339.                            bool b1, b2, b3, b4;
340.                            eObjectsIDE tmp;
341.    
342.                            if (m_szShortName == NULL) switch (id)
343.                            {
344.                                    case CHARTEVENT_CHART_CHANGE:
345.                                            x = (int)ChartGetInteger(GetInfoTerminal().ID, CHART_WIDTH_IN_PIXELS);
346.                                            y = (int)ChartGetInteger(GetInfoTerminal().ID, CHART_HEIGHT_IN_PIXELS);
347.                                            macro_AdjustMinX(m_Info.x, b1);
348.                                            macro_AdjustMinY(m_Info.y, b2);
349.                                            macro_AdjustMinX(m_Info.minx, b3);
350.                                            macro_AdjustMinY(m_Info.miny, b4);
351.                                            if (b1 || b2 || b3 || b4) AdjustTemplate();
352.                                            break;
353.                                    case CHARTEVENT_MOUSE_MOVE:
354.                                            if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) switch (tmp = CheckMousePosition(x = (int)lparam, y = (int)dparam))
355.                                            {
356.                                                    case MSG_TITLE_IDE:
357.                                                            if (sx < 0)
358.                                                            {
359.                                                                    DeleteObjectEdit();
360.                                                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
361.                                                                    sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx);
362.                                                                    sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny);
363.                                                            }
364.                                                            if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx);
365.                                                            if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my);
366.                                                            if (m_Info.IsMaximized)
367.                                                            {
368.                                                                    m_Info.x = (mx > 0 ? mx : m_Info.x);
369.                                                                    m_Info.y = (my > 0 ? my : m_Info.y);
370.                                                            }else
371.                                                            {
372.                                                                    m_Info.minx = (mx > 0 ? mx : m_Info.minx);
373.                                                                    m_Info.miny = (my > 0 ? my : m_Info.miny);
374.                                                            }
375.                                                            break;
376.                                                    case MSG_BUY_MARKET:
377.                                                    case MSG_SELL_MARKET:
378.                                                    case MSG_CLOSE_POSITION:
379.                                                            DeleteObjectEdit();
380.                                                            m_Info.ConfigChartTrade.Msg = tmp;
381.                                                            m_Info.ConfigChartTrade.uCount.TickCount = GetTickCount64();
382.                                                            break;
383.                                            }else if (sx > 0)
384.                                            {
385.                                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                                                
386.                                                    sx = sy = -1;
387.                                            }
388.                                            break;
389.                                    case CHARTEVENT_OBJECT_ENDEDIT:
390.                                            switch (obj)
391.                                            {
392.                                                    case MSG_LEVERAGE_VALUE:
393.                                                    case MSG_TAKE_VALUE:
394.                                                    case MSG_STOP_VALUE:
395.                                                            dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT));
396.                                                            if (obj == MSG_TAKE_VALUE)
397.                                                                    m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue);
398.                                                            else if (obj == MSG_STOP_VALUE)
399.                                                                    m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue);
400.                                                            else
401.                                                                    m_Info.ConfigChartTrade.Leverage = (dvalue <= 0 ? m_Info.ConfigChartTrade.Leverage : (int)MathFloor(dvalue));
402.                                                            AdjustTemplate();
403.                                                            obj = MSG_NULL;
404.                                                            ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable);
405.                                                            break;
406.                                            }
407.                                            break;
408.                                    case CHARTEVENT_OBJECT_CLICK:
409.                                            if (sparam == m_Info.szObj_Chart) switch (obj = CheckMousePosition(x = (int)lparam, y = (int)dparam))
410.                                            {
411.                                                    case MSG_DAY_TRADE:
412.                                                            m_Info.ConfigChartTrade.IsDayTrade = (m_Info.ConfigChartTrade.IsDayTrade ? false : true);
413.                                                            DeleteObjectEdit();
414.                                                            break;
415.                                                    case MSG_MAX_MIN:
416.                                                            m_Info.IsMaximized = (m_Info.IsMaximized ? false : true);
417.                                                            DeleteObjectEdit();
418.                                                            break;
419.                                                    case MSG_LEVERAGE_VALUE:
420.                                                            CreateObjectEditable(obj, m_Info.ConfigChartTrade.Leverage);
421.                                                            break;
422.                                                    case MSG_TAKE_VALUE:
423.                                                            CreateObjectEditable(obj, m_Info.FinanceTake);
424.                                                            break;
425.                                                    case MSG_STOP_VALUE:
426.                                                            CreateObjectEditable(obj, m_Info.FinanceStop);
427.                                                            break;
428.                                            }
429.                                            if (obj != MSG_NULL) AdjustTemplate();
430.                                            break;
431.                            }
432.                    }
433.//+------------------------------------------------------------------+
434.};
435.//+------------------------------------------------------------------+
436.#undef macro_NameGlobalVariable
437.//+------------------------------------------------------------------+

Исходный код класса C_ChartFloatingRAD

Может показаться, что я шучу, размещая исходный код в статье, но это далеко не шутка. На самом деле, я хочу максимально подробно объяснить, что происходит. Это сделано не для того, чтобы вы реально использовали этот код, а для того, чтобы могли его понять и создать что-то ЛУЧШЕ.

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

Прямо в самом начале у нас уже есть некоторые изменения. В строке 12 мы объявляем публичное замыкание. Фактически, это замыкание там находится, потому что нам нужно, чтобы данные были доступны вне класса. Можно подумать, что мы могли бы разместить эти же данные в другом месте, таким образом поддерживая только один тип замыкания. Но здесь я объявляю только одну структуру и одно перечисление. Перечисление нам уже давно знакомо, а вот структура новая. Между строками 14 и 26 мы находим объявление структуры, которая используется для организации необходимых данных.

Большая часть этой структуры довольно проста для понимания и почти не требует объяснений. Однако здесь есть некоторые вещи, которые выглядят довольно странно. Для чего присутствует это объединение между строками 20 и 24? И для чего строка 25? Что ж, дело в том, что объединение позволит нам легче передавать вещи. Кроме того, строка 25 будет использоваться в очень конкретный момент. Мы до этого еще дойдем, будьте спокойны.

Эта же структура упоминается в строке 38. Но помните следующее: к данным, присутствующим в классе, нельзя напрямую получить доступ за пределами класса. Поэтому нам нужно какое-то средство для доступа к ним. На самом деле мы не будем обращаться к ним напрямую. Не в индикаторе, как это видно из исходного кода индикатора. Далее у нас есть строка 49, которая выполняет интересную функцию. Она будет служить своего рода селектором, но это станет яснее позже.

В строке 106 мы собираем и адаптируем первую из переменных, объявленных в структуре, найденной в строке 38. Обратите внимание: по сути это то же самое, что мы делали ранее, но сейчас мы работаем с немного другим моделированием.

Итак, в строке 109 мы корректируем одну из переменных. Это переменная количества пунктов значения take. Обратите на это внимание, я НЕ говорю в терминах финансового значения, я говорю в терминах ПУНКТОВ. Не путайте эти две вещи. Я также не имею в виду количество пунктов, основанное на текущей торговой цене. Я говорю о пунктах в общем. Цена не имеет значения, важно, сколько пунктов смещения у нас будет. Важно также отметить, что это значение в пунктах уже будет скорректировано в финансовом выражении. Это благодаря строке 107, которая делает корректировку за нас.

В строке 110 у нас что-то аналогичное, только на этот раз мы делаем это для стоп-значения. Точно так же, как это было сделано в строке 107 для корректировки финансовых показателей перед тем, как учитывать пункты в строке 109. В строке 108 мы корректируем финансовые показатели стопа перед тем, как учитывать пункты стопа. Очень важно, чтобы вы понимали, что происходит в этот момент. Если вы не сможете это понять, у вас будут трудности с пониманием корректировки, которая будет сделана позже, в момент размещения ордеров на актив.

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

Теперь в строках 157 и 177, также из соображений практичности, мы корректируем значение еще одной переменной. До сих пор не было сделано ни одного типа доступа. Все, что мы делаем, это устанавливаем и корректируем значения переменных. Но взглянув на строку 203, мы увидим нечто, что кажется странным. Почему мы сохраняем это значение в глобальной переменной терминала? Действительно ли нам нужно это делать, или это будет пустая трата времени? На самом деле, нам необходимо это сделать. Причина в том, что при удалении индикатора Chart Trade и его повторной загрузке на график все значения в памяти теряются. А это значение, которое мы сохраняем, очень важно для нас.

Однако здесь мы только восстанавливаем те значения, которые были сохранены. Так, это же самое значение, которое мы восстанавливаем в строке 203, на самом деле было сохранено в строке 274. Вы, должно быть, заметили, что в этом объяснении я пропустил некоторые функции. Но теперь мы к ним вернемся. Начнем с конструктора. Да, теперь у нас не один, а два конструктора класса. Причина этого та же, что и в статье об индикаторе мыши. Нам нужен метод для перевода данных из буфера.

Ну, если быть честным и откровенным, нам это не так уж и необходимо. Мы могли бы выполнить перевод непосредственно в советнике или в индикаторе. Однако для удобства, я предпочитаю собрать все вместе. Таким образом, если позже потребуется внести какие-либо изменения, все, что мне нужно будет сделать, — это поработать над классом C_ChartFloatingRAD. И мне не придется заниматься всей этой работой по настройке каждой программы в отдельности, чтобы добиться стандартизации модулей. Но возвращаясь к конструкторам, у нас есть старый конструктор, который начинается со строки 221. По сути, он получает данные со строки 223, где мы инициализируем имя индикатора. Деталь: это имя используется не для записи в буфер, а для чтения из буфера. Кроме того, у нас есть строки с 232 по 234, где мы вводим новые переменные. Не все, так как остальные настраиваются в соответствии с процедурой настройки шаблона.

Второй конструктор довольно прост и начинается со строки 215. Там мы в основном присваиваем значения по умолчанию. Это происходит на этапе трансляции. Как и в случае с индикатором мыши, здесь тоже есть две фазы: одна, в которой мы записываем данные в буфер, и другая, в которой мы читаем буфер. Но это не потому, что программирование идет как-то не так. Если вы так думаете, то лишь потому, что до сих пор не поняли, что мы делаем. Фактически, при использовании индикатора мы записываем данные в буфер. Эти данные считываются через CopyBuffer какой-то другой программой, обычно советником. Вот почему у нас есть две фазы, и по той же причине у нас есть два конструктора.

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

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

Рисунок 01

Рисунок 01 – Схема коммуникации

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

Если память, где находился буфер в MetaTrader 5, освобождается из-за того, что индикатор был удален с графика, это может произойти после записи буфера. Если индикатор снова помещается на график, возможно, что буфер все еще содержит какие-то данные. Это может произойти с последним обновлением. Следовательно, когда советник читает эти данные с помощью CopyBuffer, он может прочитать их неправильно.

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

На самом деле никакой связи в терминах кода между советником и индикатором Chart Trade не существует. Эта связь осуществляется через использование буфера. Таким образом, экспертный советник не знает, что делает Chart Trade, точно так же, как Chart Trade не знает, что на самом деле делает советник. Однако MetaTrader 5 знает, что делают оба. И поскольку общей точкой между индикатором и советником является именно MetaTrader 5, мы используем его таким образом, чтобы заставить вещи происходить. Это похоже на волшебство, когда у вас или у пользователя складывается впечатление, что советник и индикатор Chart Trade — это одна и та же программа.

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

И что нам это гарантирует? А гарантирует это именно событие CHARTEVENT_MOUSE_MOVE, которое находится в строке 353 класса C_ChartFloatingRAD. Если индикатор мыши отсутствует на графике, вы сможете кликать, редактировать и изменять значения индикатора Chart Trade. Это потому, что такие события не обязательно связаны с индикатором мыши. Однако вы не сможете отправлять ордера на покупку, продажу, закрытие, или перемещать индикатор Chart Trade, если индикатор мыши не установлен на графике.

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

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

Каждый раз, когда вы кликаете или перемещаете мышь, MetaTrader 5 генерирует какое-то событие. События клика обычно обрабатываются с помощью CHARTEVENT_OBJECT_CLICK. Однако использование этого обработчика не позволяет нам поддерживать синхронизацию. Причина этого довольно сложна для объяснения, но она связана с порядком выполнения операций. Поэтому, чтобы при нажатии на одну из трёх кнопок Chart Trade (кнопка покупки, кнопка продажи и кнопка закрытия позиции) в тот же момент в советнике одновременно генерировалось событие, мы делаем это немного иначе.

Вот почему, если сравнить этот код, присутствующий в процедуре DispatchMessage, с тем же кодом из предыдущей статьи, можно увидеть, что они немного отличаются. Разница заключается в способе обработки кликов по упомянутым кнопкам. В предыдущей версии этот клик обрабатывался в событии CHARTEVENT_OBJECT_CLICK, а сейчас мы обрабатываем его в CHARTEVENT_MOUSE_MOVE. А поскольку сам код сообщает нам о том, по какому объекту был сделан клик, мы создали новую переменную для более организованной работы. Эта переменная объявляется в строке 340, и ее значение корректируется в строке 354.

Теперь обратите пристальное внимание на то, что я объясню. Между строками 376 и 378 мы размещаем коды кнопок. Таким образом, когда индикатор мыши отправляет данные нам, мы можем отправить команду на исполнение советнику. Но здесь есть маленькая деталь. Как сделать так, чтобы советник узнал, какая кнопка была нажата? Очень просто. Мы отправляем код кнопки в советник. Это делается в строке 380. Теперь в строке 381 мы фиксируем количество тиков, чтобы сгенерировать уникальный номер. Это будет необходимо для советника. Но не волнуйтесь, скоро вы поймете, как именно это работает.

Таким образом, для каждого сгенерированного события, в дополнение к этому вызову функции DispatchMessage, которая подготавливает данные для отправки в буфер, у нас будет еще один вызов. Тот, который фактически и помещает данные в буфер. Вы можете увидеть его код, начиная со строки 279 в классе. Теперь посмотрим на строку 281, где мы находим статическую переменную. Она будет хранить значение, передаваемое событием OnCalculate, то есть нам нужно сохранить значение rates_total, причина чего уже была объяснена ранее. Прочтите статьи, указанные вначале, чтобы понять, почему. Таким образом, когда произойдет вызов, осуществляемый обработчиком OnChartEvent, мы будем знать, куда поместить данные в буфер.

Обратите внимание на то, что в строке 284 мы выполняем проверку, чтобы гарантировать, что только индикатор Chart Trade будет записывать данные в память. Кроме того, запись в память будет происходить только при нажатии одной из нужных кнопок. Все это очень хорошо и прекрасно. Но ничто из этого не будет иметь никакой ценности, если советник не интерпретирует данные, отправленные ему индикатором Chart Trade. Чтобы понять, как советник сможет интерпретировать данные, нам нужно рассмотреть один функциональный код, который в то же время будет максимально базовым.


Использование тестового советника

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

Основываясь на этом критерии, мы собираемся использовать код, который вы можете увидеть ниже:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Demo version between interaction of Chart Trade and EA"
04. #property version   "1.47"
05. #property link "https://www.mql5.com/es/articles/11760"
06. //+------------------------------------------------------------------+
07. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
08. //+------------------------------------------------------------------+
09. C_ChartFloatingRAD *chart = NULL;
10. //+------------------------------------------------------------------+
11. int OnInit()
12. {
13.     chart = new C_ChartFloatingRAD("Indicator Chart Trade");
14.     
15.     return (CheckPointer(chart) != POINTER_INVALID ? INIT_SUCCEEDED : INIT_FAILED);
16. }
17. //+------------------------------------------------------------------+
18. void OnDeinit(const int reason)
19. {
20.     delete chart;
21. }
22. //+------------------------------------------------------------------+
23. void OnTick() {}
24. //+------------------------------------------------------------------+
25. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
26. {
27.     static ulong st_uTime = 0;
28.     C_ChartFloatingRAD::stData info;
29.     
30.     switch (id)
31.     {
32.             case CHARTEVENT_OBJECT_CLICK:
33.                     info = (*chart).GetDataBuffer();
34.                     if (st_uTime != info.uCount.TickCount)
35.                     {
36.                             st_uTime = info.uCount.TickCount;
37.                             PrintFormat("%u -- %s [%s] %d : %f <> %f", info.uCount.TickCount, info.IsDayTrade ? "DT" : "SW", EnumToString(info.Msg), info.Leverage, info.PointsTake, info.PointsStop);
38.                     }else Print("IGNORADO...");
39.                     break;
40.     }
41. }
42. //+------------------------------------------------------------------+

Исходный код тестового советника

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

Ну а теперь давайте быстро рассмотрим код, поскольку там всего несколько строк и его будет легко понять.

Весь тот код, который был представлен в предыдущей теме, и присутствующий в классе C_ChartFloatingRAD, суммируется при использовании здесь, в советнике. Хотя в строке 7 мы включаем класс полностью, для компилятора это не совсем так. Поэтому, в строке 9 мы глобально объявляем указатель. Он будет использоваться для доступа к классу C_ChartFloatingRAD. Это может вас немного смутить. Но только потому, что вы воображаете, что доступ к классу будет осуществляться таким же образом, как и доступ к коду индикатора.

На самом деле, можно было бы сделать это даже и таким образом, просто нецелесообразно. Причина в том, что класс не предназначен для использования без индикатора. То же самое касается класса C_Mouse, который используется в индикаторе мыши и не должен использоваться в коде, который индикатором не является. Я знаю, что у многих может возникнуть соблазн сделать это. Но НЕ следует этого делать. Поскольку вся ожидаемая безопасность, моделирование и производительность не предназначены для использования классов в коде, отличном от исходного. То есть, если вы поместите в советник тот же способ кодирования, что и в индикаторе, вы действительно сможете добиться того, что не будет необходимости в использовании индикатора Chart Trade. Это факт.

Тем не менее, если вы сделаете это, перенесете код индикатора в советник, у вас могут возникнуть проблемы со стабильностью и безопасностью всей системы, поскольку она не создана для обеспечения подобной устойчивости. Так что  всему свое место. Итак, когда мы создаем указатель в строке 13, вызывая конструктор, обратите внимание, что я буду использовать то же имя, которое было указано как имя индикатора Chart Trade. Таким образом индикатор будет выполнять свою работу, а советник  свою. Каждый на своем месте. Если что-то пойдет не так в одном из них, все, что нужно будет сделать, — это перезапустить его на графике.

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

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

Проблема в том, что в строке 33 событие клика по объекту попытается прочитать содержимое буфера индикатора Chart Trade. Если это удастся, мы получим возвращаемые данные. А затем, в строке 34, мы проверим, является ли это событием, связанным с Chart Trade, или чем-то другим, что можно игнорировать.

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

 

Заключение

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


Видео 01 – Демонстрация



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

Прикрепленные файлы |
Anexo_47.zip (175.7 KB)
Ложные регрессии в Python Ложные регрессии в Python
Ложные регрессии возникают, когда два временных ряда демонстрируют высокую степень корреляции чисто случайно, что приводит к вводящим в заблуждение результатам регрессионного анализа. В таких случаях, даже если переменные кажутся связанными, корреляция является случайной и модель может быть ненадежной.
Нейросети в трейдинге: Superpoint Transformer (SPFormer) Нейросети в трейдинге: Superpoint Transformer (SPFormer)
В данной статья предлагаем познакомиться с методом сегментации 3D-люъектов на основе Superpoint Transformer (SPFormer), который устраняет необходимость в промежуточной агрегации данных. Что ускоряет процесс сегментации и повышает производительность модели.
Оптимизация атмосферными облаками — Atmosphere Clouds Model Optimization (ACMO): Теория Оптимизация атмосферными облаками — Atmosphere Clouds Model Optimization (ACMO): Теория
Статья посвящена метаэвристическому алгоритму Atmosphere Clouds Model Optimization (ACMO), который моделирует поведение облаков для решения задач оптимизации. Алгоритм использует принципы генерации, движения и распространения облаков, адаптируясь к "погодным условиям" в пространстве решений. Статья раскрывает, как метеорологическая симуляция алгоритма находит оптимальные решения в сложном пространстве возможностей и подробно описывает этапы работы ACMO, включая подготовку "неба", рождение облаков, их перемещение и концентрацию дождя.
Возможности Мастера MQL5, которые вам нужно знать (Часть 18): Поиск нейронной архитектуры с использованием собственных векторов Возможности Мастера MQL5, которые вам нужно знать (Часть 18): Поиск нейронной архитектуры с использованием собственных векторов
Поиск нейронной архитектуры (Neural Architecture Search), автоматизированный подход к определению идеальных настроек нейронной сети, может стать преимуществом при наличии большого количества вариантов и больших наборов тестовых данных. Здесь мы рассмотрим, как этот подход можно сделать еще более эффективным с помощью парных собственных векторов (Eigen Vectors).