Español Português
preview
Моделирование рынка: В единстве — сила (I)

Моделирование рынка: В единстве — сила (I)

MetaTrader 5Тестер |
80 0
Daniel Jose
Daniel Jose

Введение

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

В предыдущей статье «Моделирование рынка: Position View (XX)» я показал, как изменить индикатор позиции, чтобы создать тень на уровне, где находилась цена. Однако у этой реализации есть небольшой недостаток. Проблема возникает, когда вы перетаскиваете тейк-профит или стоп-лосс и решаете изменить режим отображения результата сделки, например, с денежного выражения на процентное. Хотя некоторые трейдеры, возможно, захотят сделать это во время перемещения, такой возможности нет. Сначала необходимо завершить перемещение и только после этого изменить режим отображения данных. После этого потребуется снова начать перемещение, чтобы информация отображалась должным образом.

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

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

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


Разберёмся, что у нас есть на данный момент

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

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #define def_NameHLine      m_Info.szPrefixName + "#HLINE"
005. #define def_NameBtnClose   m_Info.szPrefixName + "#CLOSE"
006. #define def_NameBtnMove    m_Info.szPrefixName + "#MOVE"
007. #define def_NameInfoDirect m_Info.szPrefixName + "#DIRECT"
008. #define def_NameObjLabel   m_Info.szPrefixName + "#PROFIT"
009. #define def_NameBackGround m_Info.szPrefixName + "#BACKGROUND"
010. #define def_NameVolume     m_Info.szPrefixName + "#VOLUME"
011. //+------------------------------------------------------------------+
012. #define def_GhostColor     clrDarkGray
013. #define macro_GhostName(A) (m_Info.szPrefixName + "[GHOST]" + (A != "" ? StringSubstr(A, StringFind(A, "#")) : ""))
014. //+------------------------------------------------------------------+
015. #define macro_LineInFocus(A) ObjectSetInteger(0, def_NameHLine, OBJPROP_YSIZE, m_Info.weight = (A ? 3 : 1));
016. //+------------------------------------------------------------------+
017. #define def_PathBtns "Images\\Market Replay\\Orders\\"
018. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp"
019. #resource "\\" + def_Btn_Close;
020. //+------------------------------------------------------------------+
021. #include "..\Auxiliar\C_Mouse.mqh"
022. //+------------------------------------------------------------------+
023. #ifdef def_FontName
024.     "Why are you trying to do this?"
025. #else 
026.     #define def_FontName "Lucida Console"
027.     #define def_FontSize 10
028. #endif 
029. //+------------------------------------------------------------------+
030. class C_ElementsTrade : private C_Mouse
031. {
032.     private    :
033. //+------------------------------------------------------------------+
034.         struct stInfos
035.         {
036.             struct st_01
037.             {
038.                 short   Width,
039.                         Height,
040.                         digits;
041.             }Text;
042.             ulong       ticket;
043.             string      szPrefixName,
044.                         szDescr,
045.                         szSymbol,
046.                         szMsgGhost;
047.             EnumEvents  ev;
048.             double      price,
049.                         open,
050.                         volume,
051.                         var,
052.                         tickSize,
053.                         tpsl,
054.                         limit,
055.                         priceGhost;
056.             bool        bClick,
057.                         bIsBuy;
058.             char        weight;
059.             color       _color;
060.             int         sizeText;
061.             enum e1 {eValue, eFinance, eTicks, ePercentage} ViewMode;
062.         }m_Info;
063. //+------------------------------------------------------------------+
064.         short UpdateViewPort(const double price, short size = 0, uint ui = 0)
065.         {
066. #define macro_Local(A) {                                                                                                                                                                                                    \
067.             ChartTimePriceToXY(0, 0, 0, (A ? price : m_Info.priceGhost), x, y);                                                                                                                                \
068.             x = 125 + (m_Info.ev == evMsgClosePositionEA ? 0 : _Width + (m_Info.ev == evMsgCloseTakeProfit ? _SizeControls : (_SizeControls * 2)));                             \
069.             ObjectSetInteger(0, (A ? def_NameHLine : macro_GhostName(def_NameHLine)), OBJPROP_XDISTANCE, x);                                                                    \
070.             ObjectSetInteger(0, (A ? def_NameHLine : macro_GhostName(def_NameHLine)), OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0));               \
071.             ObjectSetInteger(0, (A ? def_NameObjLabel : macro_GhostName(def_NameObjLabel)), OBJPROP_XDISTANCE, x + 10 + (m_Info.ev == evMsgClosePositionEA ? _Width + 2 : 0));  \
072.             ObjectSetInteger(0, (A ? def_NameObjLabel : macro_GhostName(def_NameObjLabel)), OBJPROP_YDISTANCE, y - (m_Info.Text.Height / 2));                                   \
073.             ObjectSetInteger(0, (A ? def_NameBtnMove : macro_GhostName(def_NameBtnMove)), OBJPROP_XDISTANCE, x + _Width + 20);                                                  \
074.             ObjectSetInteger(0, (A ? def_NameBtnMove : macro_GhostName(def_NameBtnMove)), OBJPROP_YDISTANCE, y);                                                                \
075.             ObjectSetInteger(0, (A ? def_NameBackGround : macro_GhostName(def_NameBackGround)), OBJPROP_XDISTANCE, x - 10);                                                     \
076.             ObjectSetInteger(0, (A ? def_NameBackGround : macro_GhostName(def_NameBackGround)), OBJPROP_YDISTANCE, y - ((m_Info.Text.Height + 5) / 2)); }        
077. 
078.             static short _SizeControls;
079.             static short _Width;
080.             uint x, y;
081.             
082.             if (size > 0)
083.             {
084.                 size += (short)(ui + 8);
085.                 _SizeControls = (_SizeControls > size ? _SizeControls : size);
086.                 size = (short)(_SizeControls - ui - 12);
087.                 _Width = (_Width > size ? _Width : size);
088.             }else
089.             {
090.                 macro_Local(true);
091.                 ObjectSetInteger(0, def_NameVolume, OBJPROP_XDISTANCE, x + 10);
092.                 ObjectSetInteger(0, def_NameVolume, OBJPROP_YDISTANCE, y - (m_Info.Text.Height / 2));
093.                 ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x);
094.                 ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y);
095.                 ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + (_Width * 2) + 20);
096.                 ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y);
097.                 if (m_Info.szMsgGhost != "")
098.                     macro_Local(false)
099.             }
100.             return _Width;
101. #undef macro_Local
102.         }
103. //+------------------------------------------------------------------+
104. inline void CreateLinePrice(void)
105.         {
106. #define macro_Local(A)                                                                                              \
107.             CreateObjectGraphics(szObj = A, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault));  \
108.             ObjectSetInteger(0, A, OBJPROP_BGCOLOR, m_Info._color);                                                 \
109.             ObjectSetInteger(0, A, OBJPROP_BORDER_TYPE, BORDER_FLAT);                                               \
110.             ObjectSetInteger(0, A, OBJPROP_CORNER, CORNER_LEFT_UPPER);
111. 
112.             string szObj;
113.             
114.             macro_Local(def_NameHLine);
115.             ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH));
116.             ObjectSetString(0, szObj, OBJPROP_TOOLTIP, m_Info.szDescr);
117.             macro_LineInFocus(false);
118.             macro_Local(def_NameBackGround);
119. #undef macro_Local            
120.         }
121. //+------------------------------------------------------------------+
122. inline void CreateBoxInfo(const bool bMove)
123.         {
124.             string szObj;
125.             const char c[] = {(char)(bMove ? 'u' : (m_Info.bIsBuy ? 236 : 238)), 0};
126.             
127.             CreateObjectGraphics(szObj = (bMove ? def_NameBtnMove : def_NameInfoDirect), OBJ_LABEL, clrNONE, (EnumPriority)(ePriorityDefault));
128.             ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings");
129.             ObjectSetString(0, szObj, OBJPROP_TEXT, CharArrayToString(c));
130.             ObjectSetInteger(0, szObj, OBJPROP_COLOR, (bMove ? (m_Info.ev == evMsgCloseTakeProfit ? clrDarkGreen : clrMaroon) : (m_Info.bIsBuy ? clrDarkGreen : clrMaroon)));
131.             ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, (bMove ? 17 : 15));
132.             ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER);
133.         }
134. //+------------------------------------------------------------------+
135. inline void CreateObjectInfoText(const string szObj, const color _color)
136.         {
137.             CreateObjectGraphics(szObj, OBJ_EDIT, clrNONE, (EnumPriority)(ePriorityDefault));
138.             ObjectSetString(0, szObj, OBJPROP_FONT, def_FontName);
139.             ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, def_FontSize);
140.             ObjectSetInteger(0, szObj,    OBJPROP_COLOR, clrBlack);
141.             ObjectSetInteger(0, szObj, OBJPROP_BORDER_COLOR, _color);
142.             ObjectSetInteger(0, szObj, OBJPROP_ALIGN, ALIGN_CENTER);
143.             ObjectSetInteger(0, szObj, OBJPROP_READONLY, true);
144.         }
145. //+------------------------------------------------------------------+
146. inline void CreateButtonClose(void)
147.         {
148.             string szObj;
149.             
150.             CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityDefault));
151.             ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close);
152.             ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER);
153.         }
154. //+------------------------------------------------------------------+
155. inline void AdjustDinamic(const string szObj, const string szTxt)
156.         {
157. #define macro_Local(A)                                                                                                                      \
158.             ObjectSetInteger(0, A, OBJPROP_XSIZE, m_Info.Text.Width + h + (m_Info.ev == evMsgClosePositionEA ? m_Info.Text.Width + 8 : 0)); \
159.             ObjectSetInteger(0, A, OBJPROP_YSIZE, m_Info.Text.Height + 5);
160. 
161.             uint w, h;
162. 
163.             TextSetFont(def_FontName, def_FontSize * -10);
164.             TextGetSize(szTxt, w, h);
165.             m_Info.Text.Height = (uchar) h + 4;
166.             m_Info.Text.Width = (uchar) w + 4;
167.             m_Info.Text.Width = UpdateViewPort(0, m_Info.Text.Width, h = 32);
168.             ObjectSetInteger(0, szObj, OBJPROP_XSIZE, m_Info.Text.Width);
169.             ObjectSetInteger(0, szObj, OBJPROP_YSIZE, m_Info.Text.Height);
170.             macro_Local(def_NameBackGround);
171.             if (m_Info.szMsgGhost != "")
172.             {
173.                 macro_Local(macro_GhostName(def_NameBackGround));
174.                 ObjectSetString(0, macro_GhostName(def_NameObjLabel), OBJPROP_TEXT, m_Info.szMsgGhost);
175.             }
176. #undef macro_Local
177.         }
178. //+------------------------------------------------------------------+
179. inline void ChartChange(void)
180.         {
181.             UpdateViewPort(MathAbs(m_Info.price));
182.             m_Info.limit = (m_Info.bIsBuy ? m_Info.price - m_Info.open : m_Info.open - m_Info.price);
183.             if (m_Info.ev != evMsgClosePositionEA)
184.                 ViewValue(m_Info.limit);
185.         }
186. //+------------------------------------------------------------------+
187.         void CreateGhost(void)
188.         {
189.             ObjectSetInteger(0, def_NameHLine, OBJPROP_BGCOLOR, def_GhostColor);
190.             ObjectSetInteger(0, def_NameHLine, OBJPROP_COLOR, def_GhostColor);
191.             ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, def_GhostColor);
192.             ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BORDER_COLOR, def_GhostColor);
193.             ObjectSetInteger(0, def_NameBtnMove, OBJPROP_COLOR, def_GhostColor);
194.             ObjectSetInteger(0, def_NameBackGround, OBJPROP_BGCOLOR, def_GhostColor);
195.             ObjectSetInteger(0, def_NameBackGround, OBJPROP_COLOR, def_GhostColor);            
196.             ObjectSetString(0, def_NameHLine, OBJPROP_NAME, macro_GhostName(def_NameHLine));
197.             ObjectSetString(0, def_NameBtnMove, OBJPROP_NAME, macro_GhostName(def_NameBtnMove));
198.             ObjectSetString(0, def_NameObjLabel, OBJPROP_NAME, macro_GhostName(def_NameObjLabel));
199.             ObjectSetString(0, def_NameBackGround, OBJPROP_NAME, macro_GhostName(def_NameBackGround));
200.             m_Info.priceGhost = MathAbs(m_Info.price);
201.             m_Info.szMsgGhost = "";
202.         }
203. //+------------------------------------------------------------------+
204.     public    :
205. //+------------------------------------------------------------------+
206.         C_ElementsTrade(const ulong ticket, string szSymbol, const EnumEvents ev, color _color, char digits, double ticksize, string szDescr = "\n", const bool IsBuy = true)
207.             :C_Mouse(0, "")
208.         {
209.             ZeroMemory(m_Info);
210.             m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev));
211.             m_Info._color = _color;
212.             m_Info.szDescr = szDescr;
213.             m_Info.bIsBuy = IsBuy;
214.             m_Info.Text.digits = digits;
215.             m_Info.tickSize = ticksize;
216.             m_Info.szSymbol = szSymbol;
217.         }
218. //+------------------------------------------------------------------+
219.         ~C_ElementsTrade()
220.         {
221.             ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false);
222.             ObjectsDeleteAll(0, m_Info.szPrefixName);
223.             ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
224.         }
225. //+------------------------------------------------------------------+
226. inline void UpdatePrice(const double open, const double price, const double vol = 0, const double var = 0, const double special = -1)
227.         {
228.             m_Info.sizeText = 0;
229.             ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false);
230.             ObjectsDeleteAll(0, m_Info.szPrefixName + "#");
231.             ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
232.             m_Info.volume = (vol > 0 ? vol : m_Info.volume);
233.             m_Info.var = (var > 0 ? var : m_Info.var);
234.             m_Info.tpsl = (special >= 0 ? special : m_Info.tpsl);
235.             if (price > 0)
236.             {
237.                 CreateLinePrice();
238.                 CreateButtonClose();
239.                 CreateObjectInfoText(def_NameObjLabel, m_Info._color);
240.             }
241.             CreateBoxInfo(m_Info.ev != evMsgClosePositionEA);
242.             m_Info.open = open;
243.             m_Info.price = (price > 0 ? price : -open);
244.             if (m_Info.ev == evMsgClosePositionEA)
245.             {
246.                 CreateObjectInfoText(def_NameVolume, clrBlack);
247.                 ObjectSetInteger(0, def_NameVolume, OBJPROP_BGCOLOR, clrViolet);                
248.                 ObjectSetString(0, def_NameVolume, OBJPROP_TEXT, DoubleToString(m_Info.volume, (MathRound(m_Info.volume) != m_Info.volume ? 2 : 0)));
249.                 AdjustDinamic(def_NameVolume, "88888" + (MathRound(m_Info.volume) != m_Info.volume ? ".88" : ""));
250.             };
251.             ChartChange();
252.         }
253. //+------------------------------------------------------------------+
254.         void ViewValue(const double profit, const bool Enabled = true)
255.         {
256.             string szTxt;
257.             color  _cor;
258.             static double memSL, memTP;
259.             
260.             if (Enabled)
261.             {
262.                 switch (m_Info.ViewMode)
263.                 {
264.                     case stInfos::eValue:
265.                         szTxt = StringFormat("%." + (string)m_Info.Text.digits + "f", MathAbs(profit));
266.                         break;
267.                     case stInfos::eFinance:
268.                         szTxt = StringFormat("$ %." + (string)m_Info.Text.digits + "f", (MathAbs(profit) / m_Info.var) * m_Info.volume);
269.                         break;
270.                     case stInfos::eTicks:
271.                         szTxt = StringFormat("%d", (uint)MathRound(MathAbs(profit) / m_Info.tickSize));
272.                         break;
273.                     case stInfos::ePercentage:
274.                         szTxt = StringFormat("%.2f%%", NormalizeDouble((MathAbs(profit) / (m_Info.open ? m_Info.open : m_Info.price)) * 100, 2));
275.                         break;
276.                 }
277.                 m_Info.szMsgGhost = ((m_Info.szMsgGhost == "") && (m_Info.ev != evMsgClosePositionEA) ? szTxt : m_Info.szMsgGhost);
278.                 ObjectSetString(0, def_NameObjLabel, OBJPROP_TEXT, szTxt);
279.                 if (StringLen(szTxt) != m_Info.sizeText)
280.                 {
281.                     AdjustDinamic(def_NameObjLabel, szTxt);
282.                     m_Info.sizeText = StringLen(szTxt);
283.                 }
284.             }
285.             _cor = (m_Info.limit >= 0 ? clrPaleGreen : clrCoral);
286.             switch (m_Info.ev)
287.             {
288.                 case evMsgCloseTakeProfit:
289.                     memTP = (Enabled ? memTP : profit);
290.                     _cor = (m_Info.limit < memTP ? clrYellow : _cor);
291.                     break;
292.                 case evMsgCloseStopLoss:
293.                     memSL = (Enabled ? memSL : profit);
294.                     _cor = (m_Info.limit > memSL ? clrYellow : _cor);
295.                     break;
296.                 case evMsgClosePositionEA:
297.                     _cor = (profit >= 0 ? clrPaleGreen : clrCoral);
298.                     break;
299.             }
300.             ObjectSetInteger(0, def_NameObjLabel, OBJPROP_BGCOLOR, _cor);
301.         }
302. //+------------------------------------------------------------------+
303.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
304.         {
305.             string sz0;
306.             long _lparam = lparam;
307.             double _dparam = dparam;
308.             
309.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
310.             switch (id)
311.             {
312.                 case (CHARTEVENT_KEYDOWN):
313.                     if (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) break;
314.                     _lparam = (long) m_Info.ticket;
315.                     _dparam = 0;
316.                     EventChartCustom(0, evUpdate_Position, _lparam, 0, "");
317.                 case CHARTEVENT_CUSTOM + evMsgSetFocus:
318.                     if ((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev))
319.                     {
320.                         CreateGhost();
321.                         UpdatePrice(m_Info.open, GetPositionsMouse().Position.Price);
322.                     }else
323.                     {
324.                         ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false);
325.                         ObjectsDeleteAll(0, macro_GhostName(""));
326.                         ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
327.                     }
328.                     macro_LineInFocus((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev));
329.                     EventChartCustom(0, (ushort)(_dparam ? evHideMouse : evShowMouse), 0, 0, "");
330.                     m_Info.bClick = false;
331.                 case CHARTEVENT_CHART_CHANGE:
332.                     ChartChange();
333.                     break;
334.                 case CHARTEVENT_CUSTOM + evMsgSwapViewModePosition:
335.                     m_Info.ViewMode = (stInfos::e1)((((stInfos::e1)_dparam) + 1) & 0x03);
336.                     EventChartCustom(0, evUpdate_Position, m_Info.ticket, 0, NULL);
337.                     break;
338.                 case CHARTEVENT_OBJECT_CLICK:
339.                     sz0 = GetPositionsMouse().szObjNameClick;
340.                     if (m_Info.bClick)
341.                     {
342.                         if (sz0 == def_NameBtnMove)
343.                             EventChartCustom(0, evMsgSetFocus, m_Info.ticket, m_Info.ev, "");
344.                         if (sz0 == def_NameBtnClose)
345.                             EventChartCustom(0, (ushort) m_Info.ev, m_Info.ticket, m_Info.tpsl, m_Info.szSymbol);
346.                         if (sz0 == def_NameObjLabel)
347.                             EventChartCustom(0, evMsgSwapViewModePosition, m_Info.ticket, (double)m_Info.ViewMode, NULL);
348.                     }
349.                     m_Info.bClick = false;
350.                     break;
351.                 case CHARTEVENT_MOUSE_MOVE:
352.                     m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick);
353.                     if (m_Info.weight > 1)
354.                     {
355.                         UpdateViewPort(_dparam = GetPositionsMouse().Position.Price);
356.                         ViewValue(m_Info.limit = (m_Info.bIsBuy ? _dparam - m_Info.open : m_Info.open - _dparam));
357.                         if (m_Info.bClick)
358.                         {
359.                             if ((m_Info.ev == evMsgCloseTakeProfit) || (m_Info.ev == evMsgCloseStopLoss))
360.                                 EventChartCustom(0, (ushort)(m_Info.ev == evMsgCloseTakeProfit ? evMsgNewTakeProfit : evMsgNewStopLoss), m_Info.ticket, GetPositionsMouse().Position.Price, m_Info.szSymbol);
361.                             EventChartCustom(0, evMsgSetFocus, 0, 0, "");
362.                         }
363.                     }
364.                     break;
365.                 case CHARTEVENT_OBJECT_DELETE:
366.                     if (StringFind(sparam, m_Info.szPrefixName) < 0) break;
367.                     UpdatePrice(m_Info.open, m_Info.price);
368.                     break;
369.             }
370.         }
371. //+------------------------------------------------------------------+
372. };
373. //+------------------------------------------------------------------+
374. #undef def_GhostColor
375. #undef macro_GhostName
376. //+------------------------------------------------------------------+
377. #undef macro_LineInFocus
378. //+------------------------------------------------------------------+
379. #undef def_Btn_Close
380. #undef def_PathBtns
381. #undef def_FontName
382. #undef def_FontSize
383. //+------------------------------------------------------------------+
384. #undef def_NameVolume
385. #undef def_NameBackGround
386. #undef def_NameObjLabel
387. #undef def_NameInfoDirect
388. #undef def_NameBtnMove
389. #undef def_NameBtnClose
390. #undef def_NameHLine
391. //+------------------------------------------------------------------+

C_ElementsTrade

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

Чтобы лучше понять такое использование макросов, обратите внимание на строку 66, где определяется макрос. Этот макрос имеет локальную область действия, поэтому он удаляется в строке 101. Я хочу, чтобы вы обратили внимание на то, что мы используем макрос в двух местах: в строках 90 и 98. В этих местах компилятор берёт код макроса и вставляет его напрямую. Таким образом, с точки зрения компилятора код действительно дублируется, но для нас — нет. Если вы захотите изменить начальную позицию по координате X, достаточно будет внести это изменение только один раз — а именно в строке 68. Это изменение отразится как на тени, так и на отображаемом сегменте. Вот так просто.

Обратите также внимание, что, поскольку мы устранили любую зависимость от символа и любую связанную с ним информацию, нам больше не нужно беспокоиться о классе C_ElementsTrade при переносе индикатора в систему репликации/моделирования. С этого момента мы сосредоточим всё внимание на основном коде индикатора, который полностью приведён ниже. Имейте в виду, что речь идет о предыдущей версии — той самой, которую мы рассматривали до прошлой статьи.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. #property icon "/Images/Market Replay/Icons/Positions.ico"
004. #property description "Indicator for tracking an open position on the server."
005. #property description "This should preferably be used together with an Expert Advisor."
006. #property description "For more details see the same article."
007. #property version "1.132"
008. #property link "https://www.mql5.com/pt/articles/13523"
009. #property indicator_chart_window
010. #property indicator_plots 0
011. //+------------------------------------------------------------------+
012. #define def_ShortName "Position View"
013. //+------------------------------------------------------------------+
014. #include <Market Replay\Order System\C_ElementsTrade.mqh>
015. #include <Market Replay\Defines.mqh>
016. //+------------------------------------------------------------------+
017. input ulong user00 = 0;        //Для использования в советнике
018. //+------------------------------------------------------------------+
019. struct st00
020. {
021.     ulong     ticket;
022.     string    szShortName,
023.               szSymbol;
024.     double    priceOpen,
025.               var,
026.               tickSize;
027.     char      digits;
028.     bool      bIsBuy;
029. }m_Infos;
030. //+------------------------------------------------------------------+
031. C_ElementsTrade *Open = NULL, *Stop = NULL, *Take = NULL;
032. //+------------------------------------------------------------------+
033. bool CheckCatch(ulong ticket)
034. {
035.     double vv;
036.     
037.     ZeroMemory(m_Infos);
038.     m_Infos.szShortName = StringFormat("%I64u", m_Infos.ticket = ticket);
039.     if (!PositionSelectByTicket(m_Infos.ticket)) return false;
040.     if (ChartWindowFind(0, m_Infos.szShortName) >= 0)
041.     {
042.         m_Infos.ticket = 0;
043.         return false;
044.     }
045.     m_Infos.szSymbol = PositionGetString(POSITION_SYMBOL);
046.     m_Infos.digits = (char)SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
047.     m_Infos.tickSize = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
048.     vv = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
049.     m_Infos.var = m_Infos.tickSize / vv;
050.     IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName);
051.     EventChartCustom(0, evUpdate_Position, ticket, 0, "");
052.             
053.     return true;
054. }
055. //+------------------------------------------------------------------+
056. inline void ProfitNow(void)
057. {
058.     double ask, bid, value;
059.     
060.     ask = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_ASK);
061.     bid = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_BID);    
062.     if (Open != NULL)
063.     {
064.         (*Open).ViewValue(value = (m_Infos.bIsBuy ? bid - m_Infos.priceOpen : m_Infos.priceOpen - ask));
065.         (*Take).ViewValue(value, false);
066.         (*Stop).ViewValue(value, false);
067.     }
068. }
069. //+------------------------------------------------------------------+
070. int OnInit()
071. {
072.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
073.     if (!CheckCatch(user00))
074.     {
075.         ChartIndicatorDelete(0, 0, def_ShortName);
076.         return INIT_FAILED;
077.     }
078. 
079.     return INIT_SUCCEEDED;
080. }
081. //+------------------------------------------------------------------+
082. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
083. {
084.     ProfitNow();
085.     
086.     return rates_total;
087. }
088. //+------------------------------------------------------------------+
089. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
090. {
091.     double volume;
092.     
093.     if (Open != NULL) (*Open).DispatchMessage(id, lparam, dparam, sparam);
094.     if (Take != NULL) (*Take).DispatchMessage(id, lparam, dparam, sparam);
095.     if (Stop != NULL) (*Stop).DispatchMessage(id, lparam, dparam, sparam);
096.     switch (id)
097.     {
098.         case CHARTEVENT_CUSTOM + evUpdate_Position:
099.             if (lparam != m_Infos.ticket) break;
100.             if (!PositionSelectByTicket(m_Infos.ticket))
101.             {
102.                 ChartIndicatorDelete(0, 0, m_Infos.szShortName);
103.                 return;
104.             };
105.             if (Open == NULL) Open = new C_ElementsTrade(
106.                                                          m_Infos.ticket,
107.                                                          m_Infos.szSymbol,
108.                                                          evMsgClosePositionEA, 
109.                                                          clrRoyalBlue, 
110.                                                          m_Infos.digits, 
111.                                                          m_Infos.tickSize, 
112.                                                          StringFormat("%I64u : Position opening price.", m_Infos.ticket), 
113.                                                          m_Infos.bIsBuy = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
114.                                                          );
115.             if (Take == NULL) Take = new C_ElementsTrade(
116.                                                          m_Infos.ticket,
117.                                                          m_Infos.szSymbol,
118.                                                          evMsgCloseTakeProfit, 
119.                                                          clrForestGreen, 
120.                                                          m_Infos.digits, 
121.                                                          m_Infos.tickSize, 
122.                                                          StringFormat("%I64u : Take Profit price.", m_Infos.ticket),
123.                                                          m_Infos.bIsBuy
124.                                                          );
125.             if (Stop == NULL) Stop = new C_ElementsTrade(
126.                                                          m_Infos.ticket, 
127.                                                          m_Infos.szSymbol,
128.                                                          evMsgCloseStopLoss, 
129.                                                          clrFireBrick, 
130.                                                          m_Infos.digits, 
131.                                                          m_Infos.tickSize, 
132.                                                          StringFormat("%I64u : Stop Loss price.", m_Infos.ticket), 
133.                                                          m_Infos.bIsBuy
134.                                                          );
135.             volume = PositionGetDouble(POSITION_VOLUME);
136.             (*Open).UpdatePrice(0, m_Infos.priceOpen = PositionGetDouble(POSITION_PRICE_OPEN), volume, m_Infos.var);
137.             (*Take).UpdatePrice(m_Infos.priceOpen, PositionGetDouble(POSITION_TP), volume, m_Infos.var, PositionGetDouble(POSITION_SL));
138.             (*Stop).UpdatePrice(m_Infos.priceOpen, PositionGetDouble(POSITION_SL), volume, m_Infos.var, PositionGetDouble(POSITION_TP));
139.             ProfitNow();
140.             break;
141.     }
142.     ChartRedraw();
143. };
144. //+------------------------------------------------------------------+
145. void OnDeinit(const int reason)
146. {
147.     delete Open;
148.     delete Take;
149.     delete Stop;
150. }
151. //+------------------------------------------------------------------+

Индикатор позиции

Как вы можете видеть, здесь находится весь набор зависимостей. Этот код подробно объяснялся в предыдущих статьях, пока не достиг своего нынешнего состояния. С этого момента все изменения будут вноситься в показанный выше код. Цель состоит в том, чтобы изолировать или, точнее говоря, отделить систему репликации/моделирования от реального рынка. Однако, даже если мы проведём это разделение, индикатор позиции останется готовым к работе в обеих ситуациях. Сам индикатор не сможет определить, работает ли он в системе репликации/моделирования или подключён к реальному торговому серверу — будь то через демо-счёт или реальный счёт.

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

На первый взгляд эта задача кажется относительно простой, не так ли? Однако она оказывается гораздо более трудоёмкой, чем может показаться. Причина в том, что все обращения к PositionGetDouble, PositionGetInteger и PositionSelectByTicket являются точками блокировки для использования системы репликации/моделирования. Именно эти препятствия нам необходимо обойти, чтобы индикатор позиции действительно можно было использовать в системе репликации/моделирования.

Поэтому нам нужно найти решение, которое позволит нам начать работать эффективно. На этом этапе многие застрянут, не сумев перейти к следующему шагу реализации, поскольку это кажется невозможным. Но так ли это на самом деле? Напомню кое-что из того, что я некоторое время назад объяснял в этой же серии статей: использование SQL и взаимодействие между MetaTrader 5 и Excel. Когда я затрагивал эти темы, вы, возможно, думали, что они не имеют никакой пользы в рамках серии, посвящённой созданию системы репликации/моделирования. Зачем объяснять это в данном контексте? Что ж, если вы не изучали эти материалы, рекомендую найти их в этой серии и пересмотреть, потому что пришло время применить эти знания.


Понять выбор решения

По практическим соображениям, и не обязательно из-за сложностей программирования, мы будем использовать в системе репликации/моделирования решение на основе SQL. Дело в том, что MQL5 уже предоставляет средства для работы с SQL без необходимости прибегать к внешнему программированию. Помните, что моя цель — показать, что мы можем создать систему репликации/моделирования полностью на MQL5. Тем не менее, если вам это нужно или вы так предпочитаете, вы можете адаптировать всё так, чтобы использовать Excel вместе с MetaTrader 5 и таким образом получать метрики своих операций и исследований в системе репликации/моделирования. Однако я не буду здесь объяснять, как это делается. Мы будем напрямую использовать базу данных SQL.

Здесь может возникнуть вопрос: почему бы не использовать отдельный файл — текстовый или двоичный — для имитации сервера? Разве это не было бы проще, чем использовать базу данных SQL? Это уместный вопрос, и, в принципе, да, это было бы проще. Однако учтите следующее: если операции, выполняемые в системе репликации/моделирования, сохранялись бы в файле, созданном исключительно для этой цели, то впоследствии нам пришлось бы проделать дополнительную работу для их анализа. Если хранить всё в базе данных SQL, необходимость в этой дополнительной работе отпадает. Гораздо проще прочитать содержимое базы данных SQL, чем преобразовывать содержимое файла, специально созданного для хранения операций, выполненных в системе репликации/моделирования.

Таким образом, вопрос не в использовании внутри системы репликации/моделирования, а в том, насколько легко впоследствии прочитать то, что было сделано в системе репликации/моделирования. Именно поэтому мы будем использовать базу данных SQL.

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

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

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


Обновление системы репликации/моделирования

Первое обновление нужно выполнить в классе C_DrawImage, как показано в следующем фрагменте.

138. //+------------------------------------------------------------------+
139.         C_DrawImage(C_Terminal *ptr, string szObjName, const color cFilter, const string szFile1, const string szFile2, const bool r180 = false)
140.             {
141.                 (*ptr).CreateObjectGraphics(m_szObjName = szObjName, OBJ_BITMAP_LABEL, ePriorityDefault);
142.                 ObjectSetString(m_ID = (*ptr).GetInfoTerminal().ID, m_szObjName, OBJPROP_BMPFILE, m_szRecName = "::" + m_szObjName);
143.                 Initilize(0, cFilter, szFile1, r180);
144.                 if (szFile2 != NULL) Initilize(1, cFilter, szFile2, r180);
145.             }
146. //+------------------------------------------------------------------+

Фрагмент из C_DrawImage

Изменение находится в строке 141 и задаёт значение свойства OBJPROP_ZORDER. Без этой настройки объекты, созданные этим классом, имели бы то же значение ZORDER, что и объекты класса C_Mouse. Поскольку C_DrawImage используется в индикаторе управления, необходимо увеличить значение OBJPROP_ZORDER, чтобы класс C_Controls мог корректно перехватывать клики по объектам.

Следующее обновление потребует немного больше работы, поскольку оно затрагивает класс C_Controls. Хотя многие могут подумать, что эти изменения не нужны, на самом деле MetaTrader 5 за это время тоже получил множество обновлений и улучшений. Эти обновления и улучшения повысили стабильность программы, а также улучшили многие другие аспекты. Поэтому нам необходимо обновить систему репликации/моделирования. Тем не менее, я полагаю, что это будет последний раз, когда нам понадобится обновить систему репликации/моделирования. Ниже вы можете увидеть новый, уже обновлённый код класса C_Controls.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\C_DrawImage.mqh"
005. #include "..\Defines.mqh"
006. //+------------------------------------------------------------------+
007. #define def_PathBMP          "Images\\Market Replay\\Control\\"
008. #define def_ButtonPlay       def_PathBMP + "Play.bmp"
009. #define def_ButtonPause      def_PathBMP + "Pause.bmp"
010. #define def_ButtonCtrl       def_PathBMP + "Ctrl.bmp"
011. #define def_ButtonCtrlBlock  def_PathBMP + "Ctrl_Block.bmp"
012. #define def_ButtonPin        def_PathBMP + "Pin.bmp"
013. #resource "\\" + def_ButtonPlay
014. #resource "\\" + def_ButtonPause
015. #resource "\\" + def_ButtonCtrl
016. #resource "\\" + def_ButtonCtrlBlock
017. #resource "\\" + def_ButtonPin
018. //+------------------------------------------------------------------+
019. #define def_ObjectCtrlName(A) "MarketReplayCTRL_" + (typename(A) == "enum eObjectControl" ? EnumToString((C_Controls::eObjectControl)(A)) : (string)(A))
020. #define def_PosXObjects       120
021. //+------------------------------------------------------------------+
022. #define def_SizeButtons       32
023. #define def_ColorFilter       0xFF00FF
024. //+------------------------------------------------------------------+
025. #include "..\Auxiliar\C_Mouse.mqh"
026. //+------------------------------------------------------------------+
027. class C_Controls : private C_Mouse
028. {
029.     protected:
030.     private    :
031. //+------------------------------------------------------------------+
032.         enum eMatrixControl {eCtrlPosition, eCtrlStatus};
033.         enum eObjectControl {ePause, ePlay, eLeft, eRight, ePin, eNull, eTriState = (def_MaxPosSlider + 1)};
034. //+------------------------------------------------------------------+
035.         struct st_00
036.         {
037.             string  szBarSlider,
038.                     szBarSliderBlock;
039.             ushort  Minimal;
040.         }m_Slider;
041.         struct st_01
042.         {
043.             C_DrawImage *Btn;
044.             bool        state;
045.             short       x, y, w, h;
046.         }m_Section[eObjectControl::eNull];
047. //+------------------------------------------------------------------+
048. inline void CreteBarSlider(short x, short size)
049.             {
050.                 ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider = def_ObjectCtrlName("B1"), OBJ_RECTANGLE_LABEL, 0, 0, 0);
051.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
052.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Section[ePin].y + 11);
053.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
054.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
055.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
056.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
057.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
058.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
059.                 ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock = def_ObjectCtrlName("B2"), OBJ_RECTANGLE_LABEL, 0, 0, 0);
060.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
061.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Section[ePin].y + 6);
062.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
063.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
064.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
065.             }
066. //+------------------------------------------------------------------+
067.         void SetPlay(bool state)
068.             {
069.                 if (m_Section[ePlay].Btn == NULL)
070.                     m_Section[ePlay].Btn = new C_DrawImage(GetPointer(this), def_ObjectCtrlName(ePlay), def_ColorFilter, "::" + def_ButtonPause, "::" + def_ButtonPlay);
071.                 m_Section[ePlay].Btn.Paint(m_Section[ePlay].x, m_Section[ePlay].y, m_Section[ePlay].w, m_Section[ePlay].h, 20, (m_Section[ePlay].state = state) ? 1 : 0, state ? "Press to Pause" : "Press to Start");
072.                 if (!state) CreateCtrlSlider();
073.             }
074. //+------------------------------------------------------------------+
075.         void CreateCtrlSlider(void)
076.             {
077.                 if (m_Section[ePin].Btn != NULL) return;
078.                 CreteBarSlider(77, 436);
079.                 m_Section[eLeft].Btn = new C_DrawImage(GetPointer(this), def_ObjectCtrlName(eLeft), def_ColorFilter, "::" + def_ButtonCtrl, "::" + def_ButtonCtrlBlock);
080.                 m_Section[eRight].Btn = new C_DrawImage(GetPointer(this), def_ObjectCtrlName(eRight), def_ColorFilter, "::" + def_ButtonCtrl, "::" + def_ButtonCtrlBlock, true);
081.                 m_Section[ePin].Btn = new C_DrawImage(GetPointer(this), def_ObjectCtrlName(ePin), def_ColorFilter, "::" + def_ButtonPin, NULL);
082.                 PositionPinSlider(m_Slider.Minimal);
083.             }
084. //+------------------------------------------------------------------+
085. inline void RemoveCtrlSlider(void)
086.             {            
087.                 ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
088.                 for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++)
089.                 {
090.                     delete m_Section[c0].Btn;
091.                     m_Section[c0].Btn = NULL;
092.                 }
093.                 ObjectsDeleteAll(GetInfoTerminal().ID, def_ObjectCtrlName("B"));
094.                 ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
095.             }
096. //+------------------------------------------------------------------+
097. inline void PositionPinSlider(ushort p)
098.             {
099.                 int iL, iR;
100.                 string szMsg;
101.                 
102.                 m_Section[ePin].x = (short)(p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
103.                 iL = (m_Section[ePin].x != m_Slider.Minimal ? 0 : 1);
104.                 iR = (m_Section[ePin].x < def_MaxPosSlider ? 0 : 1);
105.                 m_Section[ePin].x += def_PosXObjects;
106.                  m_Section[ePin].x += 95 - (def_SizeButtons / 2);
107.                  for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++) if (m_Section[c0].Btn != NULL)
108.                  {
109.                      switch (c0)
110.                      {
111.                          case eLeft  : szMsg = "Previous Position";            break;
112.                          case eRight : szMsg = "Next Position";                break;
113.                          case ePin   : szMsg = "Go To: " + IntegerToString(p); break;
114.                          default      szMsg = "\n";
115.                      }
116.                     m_Section[c0].Btn.Paint(m_Section[c0].x, m_Section[c0].y, m_Section[c0].w, m_Section[c0].h, 20, (c0 == eLeft ? iL : (c0 == eRight ? iR : 0)), szMsg);
117.                 }
118. 
119.                 ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
120.             }
121. //+------------------------------------------------------------------+
122. inline eObjectControl CheckPositionMouseClick(short &x, short &y)
123.             {                
124.                 x = (short) GetPositionsMouse().Position.X_Graphics;
125.                 y = (short) GetPositionsMouse().Position.Y_Graphics;
126.                 for (eObjectControl c0 = ePlay; c0 < eNull; c0++)
127.                 {
128.                     if ((m_Section[c0].Btn != NULL) && (m_Section[c0].x <= x) && (m_Section[c0].y <= y) && 
129.                         ((m_Section[c0].x + m_Section[c0].w) >= x) && ((m_Section[c0].y + m_Section[c0].h) >= y))
130.                         return c0;
131.                 }
132.                 
133.                 return eNull;
134.             }
135. //+------------------------------------------------------------------+
136.     public    :
137. //+------------------------------------------------------------------+
138.         C_Controls(const long Arg0, const string szShortName)
139.             :C_Mouse(Arg0, "")
140.             {
141.                 if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
142.                 if (_LastError >= ERR_USER_ERROR_FIRST) return;
143.                 ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
144.                 ObjectsDeleteAll(GetInfoTerminal().ID, def_ObjectCtrlName(""));
145.                 ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
146.                 for (eObjectControl c0 = ePlay; c0 < eNull; c0++)
147.                 {
148.                     m_Section[c0].h = m_Section[c0].w = def_SizeButtons;
149.                     m_Section[c0].y = 25;
150.                     m_Section[c0].Btn = NULL;
151.                 }
152.                 m_Section[ePlay].x = def_PosXObjects;
153.                 m_Section[eLeft].x = m_Section[ePlay].x + 47;
154.                 m_Section[eRight].x = m_Section[ePlay].x + 511;
155.                 m_Slider.Minimal = eTriState;
156.             }
157. //+------------------------------------------------------------------+
158.         ~C_Controls()
159.             {
160.                 for (eObjectControl c0 = ePlay; c0 < eNull; c0++) delete m_Section[c0].Btn;
161.                 ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false);
162.                 ObjectsDeleteAll(GetInfoTerminal().ID, def_ObjectCtrlName(""));
163.             }
164. //+------------------------------------------------------------------+
165.       void SetBuffer(const int rates_total, double &Buff[])
166.          {
167.             uCast_Double info;
168.             
169.             info._16b[eCtrlPosition] = m_Slider.Minimal;
170.             info._16b[eCtrlStatus] = (ushort)(m_Slider.Minimal > def_MaxPosSlider ? m_Slider.Minimal : (m_Section[ePlay].state ? ePlay : ePause));
171.                 info._8b[def_IndexTimeFrame] = (uchar) def_IndicatorTimeFrame;
172.             if (rates_total > 0)
173.                 Buff[rates_total - 1] = info.dValue;
174.          }
175. //+------------------------------------------------------------------+
176.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
177.             {
178.                 short x, y;
179.                 static ushort iPinPosX = 0;
180.                 static short six = -1, sps;
181.                 uCast_Double info;
182.                 
183.                 C_Mouse::DispatchMessage(id, lparam, dparam, sparam);                
184.                 switch (id)
185.                 {
186.                     case (CHARTEVENT_CUSTOM + evMsgServiceSwapStatus):
187.                         SetPlay(!m_Section[ePlay].state);
188.                         if (m_Section[ePlay].state)
189.                         {
190.                             RemoveCtrlSlider();
191.                             m_Slider.Minimal = iPinPosX;
192.                         }else CreateCtrlSlider();
193.                         break;
194.                     case (CHARTEVENT_CUSTOM + evCtrlReplayInit):
195.                         info.dValue = dparam;
196.                         if ((info._8b[7] != 'D') || (info._8b[6] != 'M')) break;
197.                         iPinPosX = m_Slider.Minimal = (info._16b[eCtrlPosition] > def_MaxPosSlider ? def_MaxPosSlider : 
198.                                                                     (info._16b[eCtrlPosition] < iPinPosX ? iPinPosX : info._16b[eCtrlPosition]));
199.                         SetPlay((eObjectControl)(info._16b[eCtrlStatus]) == ePlay);
200.                         break;
201.                     case CHARTEVENT_OBJECT_DELETE:
202.                         if (StringSubstr(sparam, 0, StringLen(def_ObjectCtrlName(""))) == def_ObjectCtrlName(""))
203.                         {
204.                             if (sparam == def_ObjectCtrlName(ePlay))
205.                             {
206.                                 delete m_Section[ePlay].Btn;
207.                                 m_Section[ePlay].Btn = NULL;
208.                                 SetPlay(m_Section[ePlay].state);
209.                             }else
210.                             {
211.                                 RemoveCtrlSlider();
212.                                 CreateCtrlSlider();
213.                             }
214.                         }
215.                         break;
216.                     case CHARTEVENT_MOUSE_MOVE:
217.                         if (CheckClick(C_Mouse::eClickLeft)) switch (CheckPositionMouseClick(x, y))
218.                         {
219.                             case ePlay:
220.                                 EventChartCustom(GetInfoTerminal().ID, evMsgServiceSwapStatus, 0, 0, "");
221.                                 break;
222.                             case eLeft:
223.                                 PositionPinSlider(iPinPosX = (iPinPosX > m_Slider.Minimal ? iPinPosX - 1 : m_Slider.Minimal));
224.                                 break;
225.                             case eRight:
226.                                 PositionPinSlider(iPinPosX = (iPinPosX < def_MaxPosSlider ? iPinPosX + 1 : def_MaxPosSlider));
227.                                 break;
228.                             case ePin:
229.                                 if (six == -1)
230.                                 {
231.                                     six = x;
232.                                     sps = (short)iPinPosX;
233.                                     ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
234.                                 }
235.                                 iPinPosX = sps + x - six;
236.                                 PositionPinSlider(iPinPosX = (iPinPosX < m_Slider.Minimal ? m_Slider.Minimal : (iPinPosX > def_MaxPosSlider ? def_MaxPosSlider : iPinPosX)));
237.                                 break;
238.                         }else if (six > 0)
239.                         {
240.                             six = -1;
241.                             ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                            
242.                         }
243.                         break;
244.                 }
245.                 ChartRedraw();
246.             }
247. //+------------------------------------------------------------------+
248. };
249. //+------------------------------------------------------------------+
250. #undef def_PosXObjects
251. #undef def_ButtonPlay
252. #undef def_ButtonPause
253. #undef def_ButtonCtrl
254. #undef def_ButtonCtrlBlock
255. #undef def_ButtonPin
256. #undef def_PathBMP
257. //+------------------------------------------------------------------+

C_Controls

Я не буду вдаваться в подробности изменений. Это останется упражнением для вас: изучите код и попробуйте определить, что именно было изменено. Однако я дам вам подсказку: обратите внимание на конструктор в строке 138 и объявление класса в строке 27. Оба были изменены, как и некоторые другие части кода. Все эти изменения связаны с изменением в наследовании. Поскольку конструктор изменился, основной код тоже изменился. Ниже показан только тот фрагмент, который действительно пришлось обновить.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.133"
07. #property link "https://www.mql5.com/pt/articles/13531"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. #property indicator_buffers 1
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Service Graphics\C_Controls.mqh>
13. //+------------------------------------------------------------------+
14. C_Controls *control = NULL;
15. //+------------------------------------------------------------------+
16. input long user00 = 0;        //ID
17. //+------------------------------------------------------------------+
18. double m_Buff[];
19. int    m_RatesTotal = 0;
20. //+------------------------------------------------------------------+
21. int OnInit()
22. {
23.     if (CheckPointer(control = new C_Controls(user00, "Market Replay Control")) == POINTER_INVALID)
24.         SetUserError(C_Terminal::ERR_PointerInvalid);
25.     if ((_LastError >= ERR_USER_ERROR_FIRST) || (user00 == 0))
26.     {
27.         Print("Control indicator failed on initialization.");
28.         return INIT_FAILED;
29.     }
30.     SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
31.     ArrayInitialize(m_Buff, EMPTY_VALUE);
32.         
33.     return INIT_SUCCEEDED;
34. }
35. //+------------------------------------------------------------------+

Фрагмент индикатора управления

Обратите внимание, что нужно было изменить только строку 23. Конечно, я также изменил версию, указанную в строке 6, и ссылку на эту статью, приведённую в строке 7. Таким образом, сервис репликации/моделирования снова обновлён и готов к следующему этапу, на котором мы добавим индикатор позиции в систему репликации/моделирования.

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "This is an indicator for graphical studies using the mouse."
04. #property description "This is an integral part of the Replay / Simulator system."
05. #property description "However it can be used in the real market."
06. #property version "1.132"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/pt/articles/13531"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. //+------------------------------------------------------------------+
12. double GL_PriceClose;
13. datetime GL_TimeAdjust;
14. //+------------------------------------------------------------------+
15. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
16. //+------------------------------------------------------------------+
17. C_Study *Study         = NULL;
18. //+------------------------------------------------------------------+
19. input color user01    = clrBlack;                     //Price Line
20. input color user02    = clrPaleGreen;                 //Positive Study
21. input color user03    = clrLightCoral;                //Negative Study
22. //+------------------------------------------------------------------+
23. C_Study::eStatusMarket m_Status;
24. //+------------------------------------------------------------------+
25. int OnInit()
26. {
27.     Study = new C_Study(0, "Indicator Mouse Study", user01, user02, user03);
28.     if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
29.     MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
30.     OnBookEvent((*Study).GetInfoTerminal().szSymbol);
31.     m_Status = C_Study::eCloseMarket;
32.     
33.     return INIT_SUCCEEDED;
34. }
35. //+------------------------------------------------------------------+
36. int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], 
37.                 const double& high[], const double& low[], const double& close[], const long& tick_volume[], 
38.                 const long& volume[], const int& spread[]) 
39. {
40.     GL_PriceClose = close[rates_total - 1];
41.     if (_Symbol == def_SymbolReplay)
42.         GL_TimeAdjust = spread[rates_total - 1] & (~def_MaskTimeService);
43.     (*Study).Update(m_Status);    
44.     
45.     return rates_total;
46. }
47. //+------------------------------------------------------------------+
48. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
49. {
50.     (*Study).DispatchMessage(id, lparam, dparam, sparam);
51. 
52.     ChartRedraw();
53. }
54. //+------------------------------------------------------------------+
55. void OnBookEvent(const string &symbol)
56. {
57.     MqlBookInfo book[];
58.     C_Study::eStatusMarket loc = m_Status;
59.    
60.     if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
61.     MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
62.     m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : (symbol == def_SymbolReplay ? C_Study::eInReplay : C_Study::eInTrading));
63.     for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
64.         if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
65.     if (loc != m_Status) (*Study).Update(m_Status);
66. }
67. //+------------------------------------------------------------------+
68. void OnDeinit(const int reason)
69. {
70.     MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
71. 
72.     delete Study;
73. }
74. //+------------------------------------------------------------------+

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

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

C_Study

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_MousePrefixName "MouseBase" + (string)GetInfoTerminal().SubWin + "_"
007. #define macro_NameObjectStudy (def_MousePrefixName + "T" + (string)ObjectsTotal(0))
008. //+------------------------------------------------------------------+
009. class C_Mouse : public C_Terminal
010. {
011.     public    :
012.         enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
013.         enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
014. //+------------------------------------------------------------------+
015.     protected:
016. //+------------------------------------------------------------------+
017.         void CreateObjToStudy(int x, int w, string szName, color backColor = clrNONE) const
018.             {
019.                if (!m_Mem.IsInitOk) return;
020.                CreateObjectGraphics(szName, OBJ_BUTTON);
021.                ObjectSetInteger(0, szName, OBJPROP_STATE, true);
022.                ObjectSetInteger(0, szName, OBJPROP_BORDER_COLOR, clrBlack);
023.                ObjectSetInteger(0, szName, OBJPROP_COLOR, clrBlack);
024.                ObjectSetInteger(0, szName, OBJPROP_BGCOLOR, backColor);
025.                ObjectSetString(0, szName, OBJPROP_FONT, "Lucida Console");
026.                ObjectSetInteger(0, szName, OBJPROP_FONTSIZE, 10);
027.                ObjectSetInteger(0, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
028.                ObjectSetInteger(0, szName, OBJPROP_XDISTANCE, x);
029.                ObjectSetInteger(0, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1);
030.                ObjectSetInteger(0, szName, OBJPROP_XSIZE, w); 
031.                ObjectSetInteger(0, szName, OBJPROP_YSIZE, 18);
032.             }
033. //+------------------------------------------------------------------+
034.     private    :
035.         enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
036.         struct st01
037.         {
038.             st_Mouse Data;
039.             color    corLineH,
040.                      corTrendP,
041.                      corTrendN;
042.             eStudy   Study;
043.         }m_Info;
044.         struct st_Mem
045.         {
046.             bool     CrossHair,
047.                      IsFull,
048.                      IsInitOk;
049.             datetime dt;
050.             string   szShortName,
051.                      szLineH,
052.                      szLineV,
053.                      szLineT,
054.                      szBtnS;
055.         }m_Mem;
056. //+------------------------------------------------------------------+
057.         void GetDimensionText(const string szArg, int &w, int &h)
058.             {
059.                 TextSetFont("Lucida Console", -100, FW_NORMAL);
060.                 TextGetSize(szArg, w, h);
061.                 h += 5;
062.                 w += 5;
063.             }
064. //+------------------------------------------------------------------+
065.         void CreateStudy(void)
066.             {
067.                 if (m_Mem.IsFull)
068.                 {
069.                     CreateObjectGraphics(m_Mem.szLineV = macro_NameObjectStudy, OBJ_VLINE, m_Info.corLineH);
070.                     CreateObjectGraphics(m_Mem.szLineT = macro_NameObjectStudy, OBJ_TREND, m_Info.corLineH);
071.                     ObjectSetInteger(0, m_Mem.szLineT, OBJPROP_WIDTH, 2);
072.                     CreateObjToStudy(0, 0, m_Mem.szBtnS = macro_NameObjectStudy);
073.                 }
074.                 m_Info.Study = eStudyCreate;
075.             }
076. //+------------------------------------------------------------------+
077.         void ExecuteStudy(const double memPrice)
078.             {
079.                 double v1 = GetPositionsMouse().Position.Price - memPrice;
080.                 int w, h;
081.                 
082.                 if (!CheckClick(eClickLeft))
083.                 {
084.                     m_Info.Study = eStudyNull;
085.                     ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
086.                     if (m_Mem.IsFull) ObjectsDeleteAll(0, def_MousePrefixName + "T");
087.                 }else if (m_Mem.IsFull)
088.                 {
089.                     string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ",
090.                     MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetPositionsMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0));
091.                     GetDimensionText(sz1, w, h);
092.                     ObjectSetString(0, m_Mem.szBtnS, OBJPROP_TEXT, sz1);                                                                
093.                     ObjectSetInteger(0, m_Mem.szBtnS, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP));
094.                     ObjectSetInteger(0, m_Mem.szBtnS, OBJPROP_XSIZE, w);
095.                     ObjectSetInteger(0, m_Mem.szBtnS, OBJPROP_YSIZE, h);
096.                     ObjectSetInteger(0, m_Mem.szBtnS, OBJPROP_XDISTANCE, GetPositionsMouse().Position.X_Adjusted - w);
097.                     ObjectSetInteger(0, m_Mem.szBtnS, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h));                
098.                     ObjectMove(0, m_Mem.szLineT, 1, GetPositionsMouse().Position.dt, GetPositionsMouse().Position.Price);
099.                     ObjectSetInteger(0, m_Mem.szLineT, OBJPROP_COLOR, (memPrice > GetPositionsMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
100.                 }
101.                 m_Info.Data.ButtonStatus = eKeyNull;
102.             }
103. //+------------------------------------------------------------------+
104.     public    :
105. //+------------------------------------------------------------------+
106.         C_Mouse(const long id, const string szShortName)
107.             :C_Terminal(id)
108.             {
109.                 m_Mem.IsInitOk = false;
110.                 m_Mem.szShortName = szShortName;
111.             }
112. //+------------------------------------------------------------------+
113.         C_Mouse(const long id, const string szShortName, color corH, color corP, color corN)
114.             :C_Terminal(id)
115.             {
116.                 if (!(m_Mem.IsInitOk = IndicatorCheckPass(m_Mem.szShortName = szShortName))) return;
117.                 m_Mem.CrossHair = (bool)ChartGetInteger(0, CHART_CROSSHAIR_TOOL);
118.                 ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
119.                  ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
120.                 ZeroMemory(m_Info);
121.                 m_Info.corLineH  = corH;
122.                 m_Info.corTrendP = corP;
123.                 m_Info.corTrendN = corN;
124.                 m_Info.Study = eStudyNull;
125.                 if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE))
126.                     CreateObjectGraphics(m_Mem.szLineH = (def_MousePrefixName + (string)ObjectsTotal(0)), OBJ_HLINE, m_Info.corLineH);
127.                 ChartRedraw(0);
128.             }
129. //+------------------------------------------------------------------+
130.         ~C_Mouse()
131.             {
132.                 long loc = ChartGetInteger(0, CHART_EVENT_OBJECT_DELETE);
133.                 if (!m_Mem.IsInitOk) return;
134.                 ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, false);
135.                 ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, ChartWindowFind(0, m_Mem.szShortName) != -1);
136.                 ChartSetInteger(0, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
137.                 ObjectsDeleteAll(0, def_MousePrefixName);
138.                 ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, loc);
139.             }
140. //+------------------------------------------------------------------+
141. inline bool CheckClick(const eBtnMouse value) 
142.             {
143.                 return (m_Info.Data.ButtonStatus & value) == value;
144.             }
145. //+------------------------------------------------------------------+
146.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
147.             {
148.                 int w = 0;
149.                 static double memPrice = 0;
150.         
151.                 C_Terminal::DispatchMessage(id, lparam, dparam, sparam);
152.                 switch (id)
153.                 {
154.                     case (CHARTEVENT_CUSTOM + evHideMouse):
155.                         if (m_Mem.IsFull) ObjectSetInteger(0, m_Mem.szLineH, OBJPROP_COLOR, clrNONE);
156.                         break;
157.                     case (CHARTEVENT_CUSTOM + evShowMouse):
158.                         if (m_Mem.IsFull) ObjectSetInteger(0, m_Mem.szLineH, OBJPROP_COLOR, m_Info.corLineH);
159.                         break;
160.                     case CHARTEVENT_MOUSE_MOVE:
161.                         m_Info.Data = GetPositionsMouse();
162.                         if (m_Mem.IsFull) ObjectMove(0, m_Mem.szLineH, 0, 0, m_Info.Data.Position.Price);
163.                         if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(0, m_Mem.szLineV, 0, m_Info.Data.Position.dt, 0);
164.                         m_Info.Data.ButtonStatus = (uchar) sparam;
165.                         if (CheckClick(eClickMiddle) && (m_Info.Study != eStudyCreate))
166.                             if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(0, m_Mem.szLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
167.                         if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
168.                         {
169.                             ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
170.                             if (m_Mem.IsFull)    ObjectMove(0, m_Mem.szLineT, 0, m_Mem.dt = m_Info.Data.Position.dt, memPrice = m_Info.Data.Position.Price);
171.                             m_Info.Study = eStudyExecute;
172.                         }
173.                         if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
174.                         m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute;
175.                         break;
176.                     case CHARTEVENT_OBJECT_DELETE:
177.                         if ((m_Mem.IsFull) && (sparam == m_Mem.szLineH))
178.                             CreateObjectGraphics(m_Mem.szLineH, OBJ_HLINE, m_Info.corLineH);
179.                         break;
180.                 }
181.             }
182. //+------------------------------------------------------------------+
183. };
184. //+------------------------------------------------------------------+
185. #undef macro_NameObjectStudy
186. //+------------------------------------------------------------------+

C_Mouse

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


Обновление кода индикатора позиции

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

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. #property icon "/Images/Market Replay/Icons/Positions.ico"
004. #property description "Indicator for tracking an open position on the server."
005. #property description "This should preferably be used together with an Expert Advisor."
006. #property description "For more details see the same article."
007. #property version   "1.133"
008. #property link "https://www.mql5.com/pt/articles/13531"
009. #property indicator_chart_window
010. #property indicator_plots 0
011. //+------------------------------------------------------------------+
012. #define def_ShortName "Position View"
013. //+------------------------------------------------------------------+
014. #include <Market Replay\Order System\C_ElementsTrade.mqh>
015. #include <Market Replay\Defines.mqh>
016. //+------------------------------------------------------------------+
017. input ulong user00 = 0;        //Для использования в советнике
018. //+------------------------------------------------------------------+
019. struct st00
020. {
021.     ulong   ticket;
022.     string  szShortName,
023.             szSymbol;
024.     double  priceOpen,
025.             var,
026.             tickSize;
027.     char    digits;
028.     bool    bIsBuy;
029. }m_Infos;
030. //+------------------------------------------------------------------+
031. C_ElementsTrade *Open = NULL, *Stop = NULL, *Take = NULL;
032. //+------------------------------------------------------------------+
033. inline const double _PositionGetDouble(ENUM_POSITION_PROPERTY_DOUBLE arg)
034. {
035.     if (_Symbol != def_SymbolReplay)
036.         return PositionGetDouble(arg);
037.     
038.     return 0;
039. }
040. //+------------------------------------------------------------------+
041. inline const long _PositionGetInteger(ENUM_POSITION_PROPERTY_INTEGER arg)
042. {
043.     if (_Symbol != def_SymbolReplay)
044.         return PositionGetInteger(arg);
045.         
046.     return 0;
047. }
048. //+------------------------------------------------------------------+
049. inline const bool _PositionSelectByTicket(ulong arg)
050. {
051.     if (_Symbol != def_SymbolReplay)
052.         return PositionSelectByTicket(arg);
053.         
054.     return false;
055. }
056. //+------------------------------------------------------------------+
057. bool CheckCatch(ulong ticket)
058. {
059.     double vv;
060.     
061.     ZeroMemory(m_Infos);
062.     m_Infos.szShortName = StringFormat("%I64u", m_Infos.ticket = ticket);
063.     if (!_PositionSelectByTicket(m_Infos.ticket)) return false;
064.     if (ChartWindowFind(0, m_Infos.szShortName) >= 0)
065.     {
066.         m_Infos.ticket = 0;
067.         return false;
068.     }
069.     m_Infos.szSymbol = (_Symbol == def_SymbolReplay ? def_SymbolReplay : PositionGetString(POSITION_SYMBOL));
070.     m_Infos.digits = (char)SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
071.     m_Infos.tickSize = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
072.     vv = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
073.     m_Infos.var = m_Infos.tickSize / vv;
074.     IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName);
075.     EventChartCustom(0, evUpdate_Position, ticket, 0, "");
076.             
077.     return true;
078. }
079. //+------------------------------------------------------------------+
080. inline void ProfitNow(void)
081. {
082.     double ask, bid, value;
083.     
084.     ask = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_ASK);
085.     bid = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_BID);    
086.     if (Open != NULL)
087.     {
088.         (*Open).ViewValue(value = (m_Infos.bIsBuy ? bid - m_Infos.priceOpen : m_Infos.priceOpen - ask));
089.         (*Take).ViewValue(value, false);
090.         (*Stop).ViewValue(value, false);
091.     }
092. }
093. //+------------------------------------------------------------------+
094. int OnInit()
095. {
096.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
097.     if (!CheckCatch(user00))
098.     {
099.         ChartIndicatorDelete(0, 0, def_ShortName);
100.         return INIT_FAILED;
101.     }
102. 
103.     return INIT_SUCCEEDED;
104. }
105. //+------------------------------------------------------------------+
106. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
107. {
108.     ProfitNow();
109.     
110.     return rates_total;
111. }
112. //+------------------------------------------------------------------+
113. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
114. {
115.     double volume;
116.     
117.     if (Open != NULL) (*Open).DispatchMessage(id, lparam, dparam, sparam);
118.     if (Take != NULL) (*Take).DispatchMessage(id, lparam, dparam, sparam);
119.     if (Stop != NULL)    (*Stop).DispatchMessage(id, lparam, dparam, sparam);
120.     switch (id)
121.     {
122.         case CHARTEVENT_CUSTOM + evUpdate_Position:
123.             if (lparam != m_Infos.ticket) break;
124.             if (!_PositionSelectByTicket(m_Infos.ticket))
125.             {
126.                 ChartIndicatorDelete(0, 0, m_Infos.szShortName);
127.                 return;
128.             };
129.             if (Open == NULL) Open = new C_ElementsTrade(
130.                                                         m_Infos.ticket,
131.                                                         m_Infos.szSymbol,
132.                                                         evMsgClosePositionEA, 
133.                                                         clrRoyalBlue, 
134.                                                         m_Infos.digits, 
135.                                                         m_Infos.tickSize, 
136.                                                         StringFormat("%I64u : Position opening price.", m_Infos.ticket), 
137.                                                         m_Infos.bIsBuy = (_PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
138.                                                         );
139.             if (Take == NULL) Take = new C_ElementsTrade(
140.                                                         m_Infos.ticket,
141.                                                         m_Infos.szSymbol,
142.                                                         evMsgCloseTakeProfit, 
143.                                                         clrForestGreen, 
144.                                                         m_Infos.digits, 
145.                                                         m_Infos.tickSize, 
146.                                                         StringFormat("%I64u : Take Profit price.", m_Infos.ticket),
147.                                                         m_Infos.bIsBuy
148.                                                         );
149.             if (Stop == NULL) Stop = new C_ElementsTrade(
150.                                                         m_Infos.ticket, 
151.                                                         m_Infos.szSymbol,
152.                                                         evMsgCloseStopLoss, 
153.                                                         clrFireBrick, 
154.                                                         m_Infos.digits, 
155.                                                         m_Infos.tickSize, 
156.                                                         StringFormat("%I64u : Stop Loss price.", m_Infos.ticket), 
157.                                                         m_Infos.bIsBuy
158.                                                         );
159.             volume = _PositionGetDouble(POSITION_VOLUME);
160.             (*Open).UpdatePrice(0, m_Infos.priceOpen = _PositionGetDouble(POSITION_PRICE_OPEN), volume, m_Infos.var);
161.             (*Take).UpdatePrice(m_Infos.priceOpen, _PositionGetDouble(POSITION_TP), volume, m_Infos.var, _PositionGetDouble(POSITION_SL));
162.             (*Stop).UpdatePrice(m_Infos.priceOpen, _PositionGetDouble(POSITION_SL), volume, m_Infos.var, _PositionGetDouble(POSITION_TP));
163.             ProfitNow();
164.             break;
165.     }
166.     ChartRedraw();
167. };
168. //+------------------------------------------------------------------+
169. void OnDeinit(const int reason)
170. {
171.     delete Open;
172.     delete Take;
173.     delete Stop;
174. }
175. //+------------------------------------------------------------------+


Индикатор позиции

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

Хорошо. Основная цель этих трёх функций, расположенных в строках 33, 41 и 49, — сделать так, чтобы индикатор позиции работал одинаково в любом сценарии. Если мы подключены к реальному торговому серверу — через демо-счёт или реальный счёт, — проверки в строках 35, 43 и 51 позволят исходным функциям MQL5 получать и обновлять значения позиции, чтобы данные корректно отображались на графике. И не думайте, что я забыл о PositionGetString. Посмотрите на строку 69, и вы увидите, как я решил проблему доступа к PositionGetString. Поскольку речь идёт об очень простой вещи, создавать дополнительную процедуру не потребовалось.

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

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

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


В единстве — сила

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

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

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #define def_FileDataBase "Replay.db"
005. //+------------------------------------------------------------------+
006. #include "..\Service Graphics\Support\C_Array.mqh"
007. //+------------------------------------------------------------------+
008. class C_ReplayDataBase
009. {
010.     private    :
011. //+------------------------------------------------------------------+
012.         struct st_00
013.         {
014.             int     handle,
015.                     request;
016.         }m_Infos;
017.         C_Array     m_Arr;
018. //+------------------------------------------------------------------+
019.         void Convert(const char &buff[], const int size)
020.         {
021.             string sz0 = "";
022.             bool b0, b1, bs1, bs2, bc0, bc1, bc;
023.             int nLine = 1;
024.             
025.             b0 = b1 = bs1 = bs2 = bc0 = bc1 = bc = false;
026.             for (int count = 0, nC0 = nLine; count < size; count++)
027.             {
028.                 switch (buff[count])
029.                 {
030.                     case '\t':
031.                         sz0 += (bs1 || bs2 ? "\t" : "");
032.                         break;
033.                     case '\n':
034.                         nC0++;
035.                     case '\r':
036.                         bc0 = false;
037.                         break;
038.                     case ';':
039.                         b0 = (bs1 || bs2 || bc0 || bc1 ? b0 : true);
040.                     default:
041.                         switch (buff[count])
042.                         {
043.                             case '"':
044.                                 bs1 = (bs2 || bc0 || bc1 ? bs1 : !bs1);
045.                                 break;
046.                             case '\'':
047.                                 bs2 = (bs1 || bc0 || bc1 ? bs2 : !bs2);
048.                                 break;
049.                         }
050.                         if (((count + 1) < size) && (!bs1) && (!bs2))
051.                         {
052.                             if (bc = ((buff[count] == '-') && (buff[count + 1] == '-'))) bc0 = true;
053.                             if (bc = ((buff[count] == '/') && (buff[count + 1] == '*'))) bc1 = true;
054.                             if (bc = ((buff[count] == '*') && (buff[count + 1] == '/'))) bc1 = false;
055.                             if (bc)
056.                             {
057.                                 count += 1;
058.                                 bc = false;
059.                                 continue;
060.                             }
061.                         }
062.                         if (!(bc0 || bc1))
063.                         {
064.                             if ((!b1) && (buff[count] > ' '))
065.                             {
066.                                 b1 = true;
067.                                 nLine = nC0;
068.                             }
069.                             sz0 += (b1 ? StringFormat("%c", buff[count]) : "");
070.                         }
071.                 }
072.                 if (b0)
073.                 {
074.                     m_Arr.Add(sz0, nLine);
075.                     sz0 = "";
076.                     b0 = b1 = false;
077.                 }
078.             }
079.         }
080. //+------------------------------------------------------------------+
081.         const string ExecSQL(void)
082.         {
083.             string szCmd;
084.             
085.             for (int count = 0, nLine; count >= 0; count++)
086.             {
087.                 szCmd = m_Arr.At(count, nLine);
088.                 if (nLine < 0) break;
089.                 if (!ExecCommandSQL(szCmd))
090.                     return StringFormat("Execution of line %d of the SQL script failed...", nLine);
091.             }
092.             
093.             return NULL;
094.         }
095. //+------------------------------------------------------------------+
096.     public    :
097. //+------------------------------------------------------------------+
098.         C_ReplayDataBase()
099.         {
100.             ZeroMemory(m_Infos);
101.             m_Infos.handle = DatabaseOpen(def_FileDataBase, DATABASE_OPEN_CREATE | DATABASE_OPEN_READWRITE);
102.             m_Infos.request = INVALID_HANDLE;
103.         }
104. //+------------------------------------------------------------------+
105.         ~C_ReplayDataBase()
106.         {
107.             DatabaseClose(m_Infos.handle);
108.         }
109. //+------------------------------------------------------------------+
110.         const string ExecResourceSQL(const string szResource)
111.         {
112.             char buff[];
113.             int size;
114.             
115.             ArrayResize(buff, size = StringLen(szResource));
116.             StringToCharArray(szResource, buff);
117.             Convert(buff, size);
118.             ArrayFree(buff);
119.             
120.             return ExecSQL();
121.         }
122. //+------------------------------------------------------------------+
123.         bool ExecCommandSQL(const string szCmd)
124.         {
125.             return (m_Infos.handle == INVALID_HANDLE ? false : DatabaseExecute(m_Infos.handle, szCmd));
126.         }
127. //+------------------------------------------------------------------+
128.         bool ExecRequestOfData(const string szCmd)
129.         {
130.             if (m_Infos.request != INVALID_HANDLE) DatabaseFinalize(m_Infos.request);
131.             return ((m_Infos.request = DatabasePrepare(m_Infos.handle, szCmd)) != INVALID_HANDLE);
132.         }
133. //+------------------------------------------------------------------+
134.         template <typename T> bool GetRegisterOfRequest(T &stObject, const bool Finish = true)
135.         {
136.             if (DatabaseReadBind(m_Infos.request, stObject)) return true;
137.             if (Finish)
138.             {
139.                 DatabaseFinalize(m_Infos.request);
140.                 m_Infos.request = INVALID_HANDLE;
141.             }
142.             return false;
143.         }
144. //+------------------------------------------------------------------+
145. };
146. //+------------------------------------------------------------------+
147. #undef def_FileDataBase
148. //+------------------------------------------------------------------+

C_ReplayDataBase

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

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

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. #property icon "/Images/Market Replay/Icons/Replay - EA.ico"
004. #property description "Demo version between interaction"
005. #property description "of Chart Trade and Expert Advisor"
006. #property version   "1.133"
007. #property link "https://www.mql5.com/pt/articles/13531"
008. //+------------------------------------------------------------------+
009. #include <Market Replay\Order System\C_Orders.mqh>
010. #include <Market Replay\Auxiliar\C_Terminal.mqh>
011. #include <Market Replay\SQL\C_ReplayDataBase.mqh>
012. //+------------------------------------------------------------------+
013. enum eTypeContract {MINI, FULL};
014. //+------------------------------------------------------------------+
015. input eTypeContract user00 = MINI;         //Перекрёстный ордер в контракте
016. //+------------------------------------------------------------------+
017. C_Orders            *Orders   = NULL;
018. C_Terminal          *Terminal = NULL;
019. C_ReplayDataBase    *DB       = NULL;
020. //+------------------------------------------------------------------+
021. int OnInit()
022. {
023.     if (_Symbol == def_SymbolReplay) DB = new C_ReplayDataBase();
024.     Orders = new C_Orders(0xC0DEDAFE78514269);
025.     
026.     return INIT_SUCCEEDED;
027. }
028. //+------------------------------------------------------------------+
029. void OnTick() {}
030. //+------------------------------------------------------------------+
031. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
032. {
033.     int handle;
034.    
035.    (*Orders).DispatchMessage(id, lparam, dparam, sparam);
                                   .
                                   .
                                   .
061. }
062. //+------------------------------------------------------------------+
063. void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
064. {
                                   .
                                   .
                                   .
092. }
093. //+------------------------------------------------------------------+
094. void OnDeinit(const int reason)
095. {
                                   .
                                   .
                                   .
                                   
111.     delete Orders;
112.     delete Terminal;
113.     delete DB;
114. }
115. //+------------------------------------------------------------------+

Фрагмент советника

Здесь я хочу, чтобы вы обратили внимание на некоторые детали. В строке 11 мы добавляем заголовочный файл, который только что рассмотрели; он нужен для того, чтобы мы могли использовать базу данных SQLite. В строке 19 мы создаём указатель для доступа к классу доступа к базе данных. В строке 23 мы определяем, когда будет использоваться база данных, а точнее, когда переменная-указатель получит доступ к классу, управляющему базой данных. Наконец, в строке 113 мы освобождаем память, занимаемую указателем. По сути, это то, что мы будем делать. Тем не менее, есть некоторые различия между использованием советника, когда он подключён к реальному торговому серверу — через демо-счёт или реальный счёт, — и его использованием в системе репликации/моделирования.

Когда советник подключён к реальному серверу, весь доступ к данным позиции будет осуществляться через процедуру OnTradeTransaction. Однако при использовании системы репликации/моделирования мы не сможем работать таким же образом.

И именно здесь возникает важный момент, который нам нужно проанализировать и решить. При использовании системы репликации/моделирования мы могли бы полностью возложить всю нагрузку на советник. Но разве нет более простого способа справиться с этой ситуацией? Когда мы нажмём одну из кнопок Chart Trade, сработает пользовательское событие. Строка 35 в приведённом выше коде перехватит это событие и заставит класс C_Orders выполнить запрос. Однако одной отправки запроса недостаточно для того, чтобы индикатор позиции появился или обновился. За появление или обновление индикатора позиции отвечает процедура OnTradeTransaction. По этой причине у нас есть два сценария: в одном мы подключены к реальному серверу, а в другом такого подключения нет.

Однако если мы ограничим работу строки 35, чтобы класс C_Orders не получал события при использовании системы репликации/моделирования, то частично снизим нагрузку на советник. В таком случае мы могли бы имитировать ответ сервера, то есть событие, которое MetaTrader 5 генерирует при получении информации от сервера. Это событие вызывает выполнение OnTradeTransaction, что позволяет разместить, обновить или удалить с графика индикатор позиции.

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

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


Заключительные замечания

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

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

Файл Описание
Experts\Expert Advisor.mq5
Показывает взаимодействие между Chart Trade и советником. Для взаимодействия необходим Mouse Study.
Indicators\Chart Trade.mq5 Создаёт окно, в котором настраивается ордер, который будет отправлен. Для взаимодействия необходим Mouse Study.
Indicators\Market Replay.mq5 Создаёт необходимые элементы управления для взаимодействия с сервисом репликации/моделирования. Для взаимодействия необходим Mouse Study.
Indicators\Mouse Study.mq5 Обеспечивает взаимодействие между графическими элементами управления и пользователем. Это необходимо как для использования системы репликации/моделирования, так и для торговли на реальном рынке.
Indicators\Order Indicator.mq5 Отвечает за отображение рыночных ордеров и позволяет взаимодействовать с ними и управлять ими.
Indicators\Position View.mq5 Отвечает за отображение рыночных позиций и позволяет взаимодействовать с ними и управлять ими.
Services\Market Replay.mq5 Создаёт и поддерживает работу сервиса репликации/моделирования рынка. Это главный файл всей системы.

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

Прикрепленные файлы |
Anexo.zip (779.24 KB)
От начального до среднего уровня: Перегрузка операторов (III) От начального до среднего уровня: Перегрузка операторов (III)
В этой статье мы рассмотрим, как реализовать перегрузку как логических операторов, так и операторов сравнения. Для этого требуется определённая осторожность и немалая доля внимания. Даже небольшая оплошность при реализации перегрузки этих операторов может привести к тому, что весь код окажется совершенно непригодным для использования. Если в перегрузке возникнут какие-либо проблемы, всю базу данных, созданную на основе результатов, сгенерированных кодом, придётся либо полностью отбросить, либо, как минимум, целиком перепроверить.
Алгоритм оптимизации на основе приспособленности — Fitness Dependent Optimizer (FDO) Алгоритм оптимизации на основе приспособленности — Fitness Dependent Optimizer (FDO)
Разбираем FDO — алгоритм, вдохновлённый роением пчёл, но интересный не пчёлами. Формулы просты, но направление шага фиксирует агента на луче к глобально лучшему, сводя поиск к одной степени свободы. Мы воспроизводим канонику, сопоставляем результат с равномерным случайным перебором на том же бюджете вычислений и устраняем дефект минимальной правкой. Прилагаются готовая реализация на MQL5, полный протокол тестов и методика нуль‑модели для оценки пользы итераций.
Причинный граф как признак машинного обучения Причинный граф как признак машинного обучения
В статье условный граф причинности Грейнджера по 10 валютным парам и трём таймфреймам преобразуется в числовые признаки для CatBoost и MQL5. Абляция сравнивает модели price-only, graph-only и price+graph: графовые признаки почти не работают самостоятельно, но в сочетании с ценой улучшают контекст, режимную фильтрацию и риск. На форварде совместная модель показала более высокий Profit Factor и меньшую просадку.
Нейросети в трейдинге: Адаптация прогноза при смене рыночного режима (OMPB) Нейросети в трейдинге: Адаптация прогноза при смене рыночного режима (OMPB)
В статье разбирается фреймворк OMPB для онлайн-калибровки прогнозов при сдвиге распределения временных рядов. Рассматриваются архитектура, PAC-Bayes сертификат с учётом временной зависимости и измерение расхождения Mismatch между исходной и целевой областями. На практике начинается адаптация под MQL5: реализован байесовский линейный слой, который станет основой корректирующего модуля и позволит настраивать модель без полного переобучения.