Español Português
preview
Моделирование рынка: Position View (XII)

Моделирование рынка: Position View (XII)

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

Введение

Приветствую всех в очередной статье из серии о том, как создать систему повторения/моделирования.

В предыдущей статье Моделирование рынка: Position View (XI) мы рассмотрели, как перемещать линии стоп-лосса и тейк-профита очень простым и практичным способом, отображая на графике именно то состояние, которое зарегистрировано на реальном торговом сервере. Как вы, вероятно, заметили, существует очевидная трудность, которая влияет на индикатор позиции, когда удалены и линия стоп-лосса, и линия тейк-профита. Проблема в том, что на графике невозможно напрямую определить, является ли позиция длинной или короткой. Для этого необходимо открыть терминал. Однако мы можем решить данную проблему очень простым и практичным способом. Это первое, что мы рассмотрим в этой статье. Давайте перейдём к первой теме.


Графическое представление направления позиции

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

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

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

Первое, что нам нужно сделать, — изменить класс C_ElementsTrade, добавив в него графический объект, который и укажет, позиция длинная или короткая. Это простая и понятная задача. Приведённый ниже код демонстрирует новую версию класса 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. //+------------------------------------------------------------------+
009. #define macro_LineInFocus(A) ObjectSetInteger(0, def_NameHLine, OBJPROP_YSIZE, m_Info.weight = (A ? 3 : 1));
010. //+------------------------------------------------------------------+
011. #define def_PathBtns "Images\\Market Replay\\Orders\\"
012. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp"
013. #resource "\\" + def_Btn_Close;
014. //+------------------------------------------------------------------+
015. #include "..\Auxiliar\C_Mouse.mqh"
016. //+------------------------------------------------------------------+
017. class C_ElementsTrade : private C_Mouse
018. {
019.     private    :
020. //+------------------------------------------------------------------+
021.         struct st00
022.         {
023.             ulong       ticket;
024.             string      szPrefixName,
025.                         szDescr;
026.             EnumEvents  ev;
027.             double      price;
028.             bool        bClick,
029.                         bIsBuy;
030.             char        weight;
031.             color       _color;
032.         }m_Info;
033. //+------------------------------------------------------------------+
034.         void UpdateViewPort(void)
035.         {
036.             int x, y;
037.             
038.             ChartTimePriceToXY(0, 0, 0, m_Info.price, x, y);            
039.             x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 220 : 290));
040.             ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x);
041.             ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0));
042.             ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x);
043.             ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y);
044.             ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + 18);
045.             ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y);
046.             ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + 30);
047.             ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y);
048.         }
049. //+------------------------------------------------------------------+
050. inline void CreateLinePrice(void)
051.         {
052.             string szObj;
053.             
054.             CreateObjectGraphics(szObj = def_NameHLine, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault));
055.             macro_LineInFocus(false);
056.             ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color);
057.             ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH));
058.             ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT);
059.             ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER);
060.             ObjectSetString(0, szObj, OBJPROP_TOOLTIP, m_Info.szDescr);
061.         }
062. //+------------------------------------------------------------------+
063. inline void CreateBoxMove(void)
064.         {
065.             string szObj;
066.             
067.             CreateObjectGraphics(szObj = def_NameBtnMove, OBJ_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault));
068.             ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings");
069.             ObjectSetString(0, szObj, OBJPROP_TEXT, "u");
070.             ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, 17);
071.             ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER);
072.             ObjectSetInteger(0, szObj, OBJPROP_XSIZE, 21);
073.             ObjectSetInteger(0, szObj, OBJPROP_YSIZE, 23);
074.         }
075. //+------------------------------------------------------------------+
076. inline void CreateButtonClose(void)
077.         {
078.             string szObj;
079.             
080.             CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityDefault));
081.             ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close);
082.             ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER);
083.         }
084. //+------------------------------------------------------------------+
085. inline void CreateInfoDirect(void)
086.         {
087.             string szObj;
088.             const char c[] = {(char)(m_Info.bIsBuy ? 236 : 238), 0};
089.             
090.             CreateObjectGraphics(szObj = def_NameInfoDirect, OBJ_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault));
091.             ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings");
092.             ObjectSetString(0, szObj, OBJPROP_TEXT, CharArrayToString(c));
093.             ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, 15);
094.             ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER);
095.             ObjectSetInteger(0, szObj, OBJPROP_XSIZE, 21);
096.             ObjectSetInteger(0, szObj, OBJPROP_YSIZE, 23);
097.             ObjectSetInteger(0, szObj, OBJPROP_COLOR, (m_Info.bIsBuy ? clrForestGreen : clrFireBrick));
098.             ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER);
099.         }
100. //+------------------------------------------------------------------+
101.     public    :
102. //+------------------------------------------------------------------+
103.         C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, string szDescr = "\n", const bool IsBuy = true)
104.             :C_Mouse(0, "")
105.         {        
106.             ZeroMemory(m_Info);
107.             m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev));
108.             m_Info._color = _color;
109.             m_Info.szDescr = szDescr;
110.             m_Info.bIsBuy = IsBuy;
111.         }
112. //+------------------------------------------------------------------+
113.         ~C_ElementsTrade()
114.         {
115.             ObjectsDeleteAll(0, m_Info.szPrefixName);
116.         }
117. //+------------------------------------------------------------------+
118. inline void UpdatePrice(const double open, const double price)
119.         {
120.             m_Info.price = (price > 0 ? price : open);
121.             if (price > 0)
122.             {
123.                 CreateLinePrice();
124.                 CreateButtonClose();
125.             }else
126.                 ObjectsDeleteAll(0, m_Info.szPrefixName);
127.             if (m_Info.ev != evMsgClosePositionEA) CreateBoxMove();
128.             else CreateInfoDirect();
129.             UpdateViewPort();
130.         }
131. //+------------------------------------------------------------------+
132.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
133.         {
134.             string sz0;
135.             
136.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
137.             switch (id)
138.             {
139.                 case CHARTEVENT_CUSTOM + evMsgSetFocus:
140.                     macro_LineInFocus((m_Info.ticket == (ulong)(lparam)) && ((EnumEvents)(dparam) == m_Info.ev));
141.                     m_Info.bClick = false;
142.                 case CHARTEVENT_CHART_CHANGE:
143.                     UpdateViewPort();
144.                     break;
145.                 case CHARTEVENT_OBJECT_CLICK:
146.                     sz0 = GetPositionsMouse().szObjNameClick;
147.                     if (m_Info.bClick) switch (m_Info.ev)
148.                     {
149.                         case evMsgClosePositionEA:
150.                             if (sz0 == def_NameBtnClose)
151.                                 EventChartCustom(0, evMsgClosePositionEA, m_Info.ticket, 0, "");
152.                             break;
153.                         case evMsgCloseTakeProfit:
154.                             if (sz0 == def_NameBtnClose)
155.                                 EventChartCustom(0, evMsgCloseTakeProfit, m_Info.ticket, PositionGetDouble(POSITION_SL), PositionGetString(POSITION_SYMBOL));
156.                             else if (sz0 == def_NameBtnMove)
157.                                 EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseTakeProfit, "");
158.                             break;
159.                         case evMsgCloseStopLoss:
160.                             if (sz0 == def_NameBtnClose)
161.                                 EventChartCustom(0, evMsgCloseStopLoss, m_Info.ticket, PositionGetDouble(POSITION_TP), PositionGetString(POSITION_SYMBOL));
162.                             else if (sz0 == def_NameBtnMove)
163.                                 EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseStopLoss, "");
164.                             break;
165.                     }
166.                     m_Info.bClick = false;
167.                     break;
168.                 case CHARTEVENT_MOUSE_MOVE:
169.                     m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick);
170.                     if (m_Info.bClick && (m_Info.weight > 1))
171.                     {
172.                         switch (m_Info.ev)
173.                         {
174.                             case evMsgCloseTakeProfit:
175.                                 EventChartCustom(0, evMsgNewTakeProfit, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL));
176.                                 break;
177.                             case evMsgCloseStopLoss:
178.                                 EventChartCustom(0, evMsgNewStopLoss, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL));
179.                                 break;
180.                         }
181.                         EventChartCustom(0, evMsgSetFocus, 0, 0, "");
182.                     }
183.                     break;
184.             }
185.         }
186. //+------------------------------------------------------------------+
187. };
188. //+------------------------------------------------------------------+
189. #undef macro_LineInFocus
190. //+------------------------------------------------------------------+
191. #undef def_Btn_Close
192. #undef def_PathBtns
193. //+------------------------------------------------------------------+
194. #undef def_NameInfoDirect
195. #undef def_NameBtnMove
196. #undef def_NameBtnClose
197. #undef def_NameHLine
198. //+------------------------------------------------------------------+

C_ElementsTrade.mqh

Если вам не удаётся идентифицировать изменения, я выделю некоторые конкретные модификации, чтобы сделать их более очевидными. Однако в идеале вы, уважаемый читатель, должны попытаться понять код без необходимости читать объяснения, которые я собираюсь предоставить. Так вы привыкнете понимать код и самостоятельно определять те части, которые могут вас заинтересовать. Отлично, в строке 07 мы находим определение имени, которое получит создаваемый объект. В строке 29 мы определяем новую переменную. Пока что он будет использоваться очень редко, поэтому может показаться излишним объявлять его. Позже вы лучше поймёте причину его объявления. Теперь, переходя к процедуре для строки 34, мы обнаруживаем две новые строки: 44 и 45. Они определяют положение объекта на графике.

Теперь перейдём к строке 85, где процедура CreateInfoDirect создаёт графический объект. Есть одна деталь, которую нам нужно понять. Если вы решите использовать изображение для представления длинной или короткой позиции, эту процедуру, начиная со строки 85, потребуется слегка изменить. В этом нет ничего сложного. Используйте код CreateButtonClose в качестве основы для загрузки изображения. Однако здесь мы будем использовать решение, аналогичное тому, которое использовалось в процедуре CreateBoxMove. Другими словами, мы поставим символ, который указывает, является ли позиция длинной или короткой. Один важный момент: на данный момент большая часть кода CreateBoxMove также будет присутствовать в CreateInfoDirect. Здесь показано, как создавать данные элементы. Впоследствии это дублирование кода исчезнет.

Давайте теперь проанализируем этот раздел, поскольку некоторые инструкции могут быть сложными для понимания. Например, выражение в строке 88 может вызвать вопросы. Теперь ответьте честно: когда вы смотрите на данную строку 88, понимаете ли вы, что происходит? Если это именно так, то всё в порядке. Это значит, что вы уже знаете то, что я собираюсь объяснить. Если это не так, то будьте внимательны. Строка 92 присваивает графическому объекту последовательность символов. Чтобы сгенерировать её, функция CharArrayToString преобразует массив "с" в строку, определённую в строке 88.

Почему я определяю это значение как константу? Потому что хочу убедиться в том, что случайно не изменю значение этого массива, и в том, что это останется неизменным на протяжении выполнения всей процедуры. Хотя данная процедура проста, в будущем она может немного усложниться. И я не хочу рисковать случайно изменить значение массива "c". А теперь начинается волшебство. Строка в MQL5 следует тому же принципу, что и строки в C/C++, то есть заканчивается нулевым символом, или \0. Это нулевое значение, которое вы видите в строке 88. Массив "c" — это одномерный динамический массив, начальный размер которого составляет два элемента: код отображаемого символа и завершающий ноль. Его динамический характер обусловлен тем, что первое измерение объявлено без фиксированного размера; начальный размер, напротив, определяется двумя значениями, включёнными в инициализацию. Без этого синтаксиса код завершится ошибкой во время компиляции.

То есть, в объявлении мы определяем "c" как одномерный динамический массив, элементы которого имеют тип ushort. После знака равенства начинается инициализация, ограниченная фигурными скобками. Внутри этих фигурных скобок выражение (ushort) не объявляет переменную и не изменяет объявление массива: оно явно преобразует к типу ushort результат выражения, вычисляемого с помощью тернарного оператора. Данное преобразование предотвращает генерацию предупреждений компилятором во время компиляции. Здесь мы используем значение переменной, определенной в строке 29. Если оно истинно, первый элемент массива хранит числовой код 236; если ложно — хранит числовой код 238. Сами по себе эти числа не являются текстом: они идентифицируют символы. Функция CharArrayToString преобразует массив в строку, содержащую выбранный символ. Строка 91 назначает Wingdings в качестве шрифта графического объекта, поэтому шрифт отображает этот символ с помощью глифа, соответствующего коду 236 или 238. Чтобы узнать, какой символ появится на графике, проверьте эти коды в таблице символов Wingdings. Остальная часть процедуры использует стандартные операции MQL5 для создания и настройки графических объектов.

Хорошо, вы, вероятно, задаётесь вопросом: "как мы укажем классу, какой из двух глифов он должен отображать для представления длинной или короткой позиции?" Ответ прост: мы передадим направление позиции через конструктор. По этой причине рассмотрите код конструктора в строке 103. Обратите внимание, что он был слегка изменён, но это не настолько существенно, чтобы требовать отдельного объяснения. Поскольку данный конструктор был изменен, нам также необходимо изменить код, который его использует. Прежде чем мы перейдем к рассмотрению изменений, давайте ещё раз взглянем на них. Я имею в виду код в строке 118. Строка 128 отличает линию цены открытия от линий тейк-профита и стоп-лосса. Условие в строке 127 определяет, представляет ли экземпляр линию цены открытия или одну из линий тейк-профита и стоп-лосса. Когда экземпляр представляет цену открытия, код не создаёт графический объект для перемещения линии. Вместо этого он создаёт объект, который показывает, является ли позиция длинной или короткой. Таким образом, мы можем завершить разработку кода для класса C_ElementsTrade. Как я недавно упоминал, произошло изменение в коде, который вызывает конструктор. Следовательно, нам необходимо изменить код класса C_IndicatorPosition. Поскольку большая часть кода осталась нетронутой, мы сосредоточимся только на том, что изменилось. Итак, изменённая часть показана в следующем фрагменте:

45. //+------------------------------------------------------------------+
46.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
47.         {
48.             double value;
49.             
50.             if (Open != NULL) (*Open).DispatchMessage(id, lparam, dparam, sparam);
51.             if (Take != NULL) (*Take).DispatchMessage(id, lparam, dparam, sparam);
52.             if (Stop != NULL) (*Stop).DispatchMessage(id, lparam, dparam, sparam);
53.             switch (id)
54.             {
55.                 case CHARTEVENT_CUSTOM + evUpdate_Position:
56.                     if (lparam != m_Infos.ticket) return;
57.                     if (!PositionSelectByTicket(m_Infos.ticket))
58.                     {
59.                         ChartIndicatorDelete(0, 0, m_Infos.szShortName);
60.                         return;
61.                     };
62.                     if (Open == NULL) Open = new C_ElementsTrade(m_Infos.ticket, evMsgClosePositionEA, clrRoyalBlue, StringFormat("%I64u : Position opening price.", m_Infos.ticket), PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY);
63.                     if (Take == NULL) Take = new C_ElementsTrade(m_Infos.ticket, evMsgCloseTakeProfit, clrForestGreen, StringFormat("%I64u : Take Profit price.", m_Infos.ticket));
64.                     if (Stop == NULL) Stop = new C_ElementsTrade(m_Infos.ticket, evMsgCloseStopLoss, clrFireBrick, StringFormat("%I64u : Stop Loss price.", m_Infos.ticket));
65.                     (*Open).UpdatePrice(0, value = PositionGetDouble(POSITION_PRICE_OPEN));
66.                     (*Take).UpdatePrice(value, PositionGetDouble(POSITION_TP));
67.                     (*Stop).UpdatePrice(value, PositionGetDouble(POSITION_SL));
68.                     break;
69.             }
70.             ChartRedraw();
71.         }
72. //+------------------------------------------------------------------+

Фрагмент файла C_IndicatorPosition.mqh

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

Выделенная область на этом изображении показывает различие. Теперь на графике четко видно, является ли позиция длинной или короткой. Здорово, не правда ли? Всё довольно просто. Прежде чем перейти к следующему изменению, давайте посмотрим, как выглядит окончательный код класса 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. //+------------------------------------------------------------------+
009. #define macro_LineInFocus(A) ObjectSetInteger(0, def_NameHLine, OBJPROP_YSIZE, m_Info.weight = (A ? 3 : 1));
010. //+------------------------------------------------------------------+
011. #define def_PathBtns "Images\\Market Replay\\Orders\\"
012. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp"
013. #resource "\\" + def_Btn_Close;
014. //+------------------------------------------------------------------+
015. #include "..\Auxiliar\C_Mouse.mqh"
016. //+------------------------------------------------------------------+
017. class C_ElementsTrade : private C_Mouse
018. {
019.     private    :
020. //+------------------------------------------------------------------+
021.         struct st00
022.         {
023.             ulong       ticket;
024.             string      szPrefixName,
025.                         szDescr;
026.             EnumEvents  ev;
027.             double      price;
028.             bool        bClick,
029.                         bIsBuy;
030.             char        weight;
031.             color       _color;
032.         }m_Info;
033. //+------------------------------------------------------------------+
034.         void UpdateViewPort(void)
035.         {
036.             int x, y;
037.             
038.             ChartTimePriceToXY(0, 0, 0, m_Info.price, x, y);            
039.             x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 220 : 290));
040.             ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x);
041.             ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0));
042.             ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x);
043.             ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y);
044.             ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + 18);
045.             ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y);
046.             ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + 30);
047.             ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y);
048.         }
049. //+------------------------------------------------------------------+
050. inline void CreateLinePrice(void)
051.         {
052.             string szObj;
053.             
054.             CreateObjectGraphics(szObj = def_NameHLine, OBJ_RECTANGLE_LABEL, m_Info._color, (EnumPriority)(ePriorityDefault));
055.             macro_LineInFocus(false);
056.             ObjectSetInteger(0, szObj, OBJPROP_BGCOLOR, m_Info._color);
057.             ObjectSetInteger(0, szObj, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH));
058.             ObjectSetInteger(0, szObj, OBJPROP_BORDER_TYPE, BORDER_FLAT);
059.             ObjectSetInteger(0, szObj, OBJPROP_CORNER, CORNER_LEFT_UPPER);
060.             ObjectSetString(0, szObj, OBJPROP_TOOLTIP, m_Info.szDescr);
061.         }
062. //+------------------------------------------------------------------+
063. inline void CreateBoxInfo(const bool bMove)
064.         {
065.             string szObj;
066.             const char c[] = {(char)(bMove ? 'u' : (m_Info.bIsBuy ? 236 : 238)), 0};
067.             
068.             CreateObjectGraphics(szObj = (bMove ? def_NameBtnMove : def_NameInfoDirect), OBJ_LABEL, clrNONE, (EnumPriority)(ePriorityDefault));
069.             ObjectSetString(0, szObj, OBJPROP_FONT, "Wingdings");
070.             ObjectSetString(0, szObj, OBJPROP_TEXT, CharArrayToString(c));
071.             ObjectSetInteger(0, szObj, OBJPROP_COLOR, (bMove ? m_Info._color : (m_Info.bIsBuy ? clrForestGreen : clrFireBrick)));
072.             ObjectSetInteger(0, szObj, OBJPROP_FONTSIZE, (bMove ? 17 : 15));
073.             ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER);
074.         }
075. //+------------------------------------------------------------------+
076. inline void CreateButtonClose(void)
077.         {
078.             string szObj;
079.             
080.             CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityDefault));
081.             ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close);
082.             ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER);
083.         }
084. //+------------------------------------------------------------------+
085.     public    :
086. //+------------------------------------------------------------------+
087.         C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, string szDescr = "\n", const bool IsBuy = true)
088.             :C_Mouse(0, "")
089.         {        
090.             ZeroMemory(m_Info);
091.             m_Info.szPrefixName = StringFormat("%I64u@%03d", m_Info.ticket = ticket, (int)(m_Info.ev = ev));
092.             m_Info._color = _color;
093.             m_Info.szDescr = szDescr;
094.             m_Info.bIsBuy = IsBuy;
095.         }
096. //+------------------------------------------------------------------+
097.         ~C_ElementsTrade()
098.         {
099.             ObjectsDeleteAll(0, m_Info.szPrefixName);
100.         }
101. //+------------------------------------------------------------------+
102. inline void UpdatePrice(const double open, const double price)
103.         {
104.             m_Info.price = (price > 0 ? price : open);
105.             if (price > 0)
106.             {
107.                 CreateLinePrice();
108.                 CreateButtonClose();
109.             }else
110.                 ObjectsDeleteAll(0, m_Info.szPrefixName);
111.             CreateBoxInfo(m_Info.ev != evMsgClosePositionEA);
112.             UpdateViewPort();
113.         }
114. //+------------------------------------------------------------------+
115.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
116.         {
117.             string sz0;
118.             
119.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
120.             switch (id)
121.             {
122.                 case CHARTEVENT_CUSTOM + evMsgSetFocus:
123.                     macro_LineInFocus((m_Info.ticket == (ulong)(lparam)) && ((EnumEvents)(dparam) == m_Info.ev));
124.                     m_Info.bClick = false;
125.                 case CHARTEVENT_CHART_CHANGE:
126.                     UpdateViewPort();
127.                     break;
128.                 case CHARTEVENT_OBJECT_CLICK:
129.                     sz0 = GetPositionsMouse().szObjNameClick;
130.                     if (m_Info.bClick) switch (m_Info.ev)
131.                     {
132.                         case evMsgClosePositionEA:
133.                             if (sz0 == def_NameBtnClose)
134.                                 EventChartCustom(0, evMsgClosePositionEA, m_Info.ticket, 0, "");
135.                             break;
136.                         case evMsgCloseTakeProfit:
137.                             if (sz0 == def_NameBtnClose)
138.                                 EventChartCustom(0, evMsgCloseTakeProfit, m_Info.ticket, PositionGetDouble(POSITION_SL), PositionGetString(POSITION_SYMBOL));
139.                             else if (sz0 == def_NameBtnMove)
140.                                 EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseTakeProfit, "");
141.                             break;
142.                         case evMsgCloseStopLoss:
143.                             if (sz0 == def_NameBtnClose)
144.                                 EventChartCustom(0, evMsgCloseStopLoss, m_Info.ticket, PositionGetDouble(POSITION_TP), PositionGetString(POSITION_SYMBOL));
145.                             else if (sz0 == def_NameBtnMove)
146.                                 EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseStopLoss, "");
147.                             break;
148.                     }
149.                     m_Info.bClick = false;
150.                     break;
151.                 case CHARTEVENT_MOUSE_MOVE:
152.                     m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick);
153.                     if (m_Info.bClick && (m_Info.weight > 1))
154.                     {
155.                         switch (m_Info.ev)
156.                         {
157.                             case evMsgCloseTakeProfit:
158.                                 EventChartCustom(0, evMsgNewTakeProfit, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL));
159.                                 break;
160.                             case evMsgCloseStopLoss:
161.                                 EventChartCustom(0, evMsgNewStopLoss, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL));
162.                                 break;
163.                         }
164.                         EventChartCustom(0, evMsgSetFocus, 0, 0, "");
165.                     }
166.                     break;
167.             }
168.         }
169. //+------------------------------------------------------------------+
170. };
171. //+------------------------------------------------------------------+
172. #undef macro_LineInFocus
173. //+------------------------------------------------------------------+
174. #undef def_Btn_Close
175. #undef def_PathBtns
176. //+------------------------------------------------------------------+
177. #undef def_NameInfoDirect
178. #undef def_NameBtnMove
179. #undef def_NameBtnClose
180. #undef def_NameHLine
181. //+------------------------------------------------------------------+

C_ElementsTrade.mqh

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

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


Какую линию я перемещаю: тейк-профита или стоп-лосса?

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

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

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

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

Второй клик отправляет советнику окончательный уровень цены для этой линии. Индикатор мыши передает выбранный ценовой уровень индикатору позиции, который генерирует событие, получаемое советником. Затем советник запрашивает у торгового сервера обновление уровня цены для линии тейк-профита или стоп-лосса. Уровень линии остаётся прежним до тех пор, пока сервер не подтвердит изменение. После получения подтверждения советник отправляет проверенный уровень на индикатор позиции, который обновляет линию на графике. Таким образом, видимый результат зависит от данной последовательности событий и ответа сервера. Первое, что нам нужно сделать, — отправить каждому компоненту соответствующее сообщение для обновления его состояния, не изменяя текущего хода событий. Чтобы вам было понятнее то, как происходит взаимодействие, мы будем вносить изменения шаг за шагом. Сначала мы сделаем следующее: когда вы кликнете по одному из графических объектов для перемещения ценовой линии, мы скроем горизонтальную линию индикатора мыши. Как только вы выберете новый уровень цены для линии тейк-профита или стоп-лосса, мы снова покажем горизонтальную линию индикатора мыши. Проще некуда.

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

114. //+------------------------------------------------------------------+
115.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
116.         {
117.             string sz0;
118.             
119.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
120.             switch (id)
121.             {
122.                 case CHARTEVENT_CUSTOM + evMsgSetFocus:
123.                     macro_LineInFocus((m_Info.ticket == (ulong)(lparam)) && ((EnumEvents)(dparam) == m_Info.ev));
124.                     EventChartCustom(0, (ushort)(dparam ? evHideMouse : evShowMouse), 0, 0, "");
125.                     m_Info.bClick = false;
126.                 case CHARTEVENT_CHART_CHANGE:
127.                     UpdateViewPort();
128.                     break;
129.                 case CHARTEVENT_OBJECT_CLICK:
130.                     sz0 = GetPositionsMouse().szObjNameClick;
131.                     if (m_Info.bClick) switch (m_Info.ev)
132.                     {

Фрагмент из C_ElementsTrade

Предыдущий фрагмент наглядно демонстрирует, какие именно изменения нам необходимо внести. Вот именно: мы модифицируем процедуру DispatchMessage. Если не присматриваться, то сложно заметить, где именно находится модификация. Это прямо на строке 124. Данная строка передает индикатору мыши значение перечисления, определяющее, следует ли отображать или скрывать горизонтальную линию. Проще некуда. Обратите внимание, что выражение (ushort) не является объявлением, а явным приведением типа, применяемым к значению перечисления, которое обычно имеет тип int. Нам нужно преобразовать это значение в ushort; в противном случае компилятор предупредит о возможной потере данных.

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

Как мы хотим представить данную вспомогательную ценовую линию? На мой взгляд, лучший способ — использовать оригинальные цвета линий. Так будет ясно, что мы перемещаем именно нужную линию. Есть ещё один момент. Простое перемещение линии кажется слишком лёгким и даже скучным. Что вы скажете, если мы немного усложним задачу и также переместим графические элементы управления, связанные с этой линией, такие как кнопка закрытия и элемент управления, используемый для её перемещения? "Хм, теперь это кажется немного сложнее. Неужели это действительно так?" Хорошо, давайте посмотрим. Если идея заключается в перемещении всех объектов вместе с ценовой линией, достаточно будет передать новый уровень цены в процедуру UpdateViewPort. "Хм, с таким кодом это будет невозможно". А что произойдет, если мы изменим процедуру UpdateViewPort таким образом, чтобы она получала цену?" Новый код показан в следующем фрагменте:

33. //+------------------------------------------------------------------+
34.         void UpdateViewPort(const double price)
35.         {
36.             int x, y;
37.             
38.             ChartTimePriceToXY(0, 0, 0, price, x, y);            
39.             x = (m_Info.ev == evMsgClosePositionEA ? 150 : (m_Info.ev == evMsgCloseTakeProfit ? 220 : 290));
40.             ObjectSetInteger(0, def_NameHLine, OBJPROP_XDISTANCE, x);
41.             ObjectSetInteger(0, def_NameHLine, OBJPROP_YDISTANCE, y - (m_Info.weight > 1 ? (int)(m_Info.weight / 2) : 0));
42.             ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, x);
43.             ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y);
44.             ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_XDISTANCE, x + 18);
45.             ObjectSetInteger(0, def_NameInfoDirect, OBJPROP_YDISTANCE, y);
46.             ObjectSetInteger(0, def_NameBtnMove, OBJPROP_XDISTANCE, x + 30);
47.             ObjectSetInteger(0, def_NameBtnMove, OBJPROP_YDISTANCE, y);
48.         }
49. //+------------------------------------------------------------------+

Фрагмент из C_ElementsTrade

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

101. //+------------------------------------------------------------------+
102. inline void UpdatePrice(const double open, const double price)
103.         {
104.             if (price > 0)
105.             {
106.                 CreateLinePrice();
107.                 CreateButtonClose();
108.             }else
109.                 ObjectsDeleteAll(0, m_Info.szPrefixName);
110.             CreateBoxInfo(m_Info.ev != evMsgClosePositionEA);
111.             UpdateViewPort(m_Info.price = (price > 0 ? price : open));
112.         }
113. //+------------------------------------------------------------------+

Фрагмент из C_ElementsTrade

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

113. //+------------------------------------------------------------------+
114.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
115.         {
116.             string sz0;
117.             
118.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
119.             switch (id)
120.             {
121.                 case CHARTEVENT_CUSTOM + evMsgSetFocus:
122.                     macro_LineInFocus((m_Info.ticket == (ulong)(lparam)) && ((EnumEvents)(dparam) == m_Info.ev));
123.                     EventChartCustom(0, (ushort)(dparam ? evHideMouse : evShowMouse), 0, 0, "");
124.                     m_Info.bClick = false;
125.                 case CHARTEVENT_CHART_CHANGE:
126.                     UpdateViewPort(m_Info.price);
127.                     break;
128.                 case CHARTEVENT_OBJECT_CLICK:
129.                     sz0 = GetPositionsMouse().szObjNameClick;
130.                     if (m_Info.bClick) switch (m_Info.ev)
131.                     {
132.                         case evMsgClosePositionEA:
133.                             if (sz0 == def_NameBtnClose)
134.                                 EventChartCustom(0, evMsgClosePositionEA, m_Info.ticket, 0, "");
135.                             break;
136.                         case evMsgCloseTakeProfit:
137.                             if (sz0 == def_NameBtnClose)
138.                                 EventChartCustom(0, evMsgCloseTakeProfit, m_Info.ticket, PositionGetDouble(POSITION_SL), PositionGetString(POSITION_SYMBOL));
139.                             else if (sz0 == def_NameBtnMove)
140.                                 EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseTakeProfit, "");
141.                             break;
142.                         case evMsgCloseStopLoss:
143.                             if (sz0 == def_NameBtnClose)
144.                                 EventChartCustom(0, evMsgCloseStopLoss, m_Info.ticket, PositionGetDouble(POSITION_TP), PositionGetString(POSITION_SYMBOL));
145.                             else if (sz0 == def_NameBtnMove)
146.                                 EventChartCustom(0, evMsgSetFocus, m_Info.ticket, evMsgCloseStopLoss, "");
147.                             break;
148.                     }
149.                     m_Info.bClick = false;
150.                     break;
151.                 case CHARTEVENT_MOUSE_MOVE:
152.                     m_Info.bClick = (CheckClick(C_Mouse::eClickLeft) ? true : m_Info.bClick);
153.                     if (m_Info.weight > 1)
154.                     {
155.                         UpdateViewPort(GetPositionsMouse().Position.Price);
156.                         if (m_Info.bClick)
157.                         {
158.                             switch (m_Info.ev)
159.                             {
160.                                 case evMsgCloseTakeProfit:
161.                                     EventChartCustom(0, evMsgNewTakeProfit, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL));
162.                                     break;
163.                                 case evMsgCloseStopLoss:
164.                                     EventChartCustom(0, evMsgNewStopLoss, m_Info.ticket, GetPositionsMouse().Position.Price, PositionGetString(POSITION_SYMBOL));
165.                                     break;
166.                             }
167.                             EventChartCustom(0, evMsgSetFocus, 0, 0, "");
168.                         }
169.                     }
170.                     break;
171.             }
172.         }
173. //+------------------------------------------------------------------+

Фрагмент из C_ElementsTrade

Теперь обратите внимание. Когда линия выделена, переменная weight будет иметь значение больше единицы. Следовательно, логическое выражение в строке 153 вернёт true, и программа выполнит соответствующий блок. Внутри этого блока строка 155 вызывает процедуру UpdateViewPort и передаёт ей новый предварительный ценовой уровень для линии. Отображение обновляется в реальном времени, но изменение подтверждается только при нажатии. Как только сервер подтвердит уровень цен, система возобновит работу.

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

Давайте посмотрим, как решить данную проблему. Итак, когда мы начинаем перемещать одну из ценовых линий, будь то тейк-профит или стоп-лосс, мы не можем начать исследование. Чтобы начать исследование с использованием индикатора мыши, обратитесь к предыдущим статьям, чтобы узнать, как это сделать. Для продолжения нужно будет отменить перемещение и решить, как его осуществить. После разговора с некоторыми людьми они предложили мне использовать нечто довольно практичное и надёжное: нажатие клавиши ESC. Мы точно можем случайно нажать кнопку мыши или другой элемент управления, но мы точно не нажмем клавишу ESC, не заметив этого. Следовательно, мы снова модифицируем процедуру DispatchMessage, чтобы клавиша ESC отменяла перемещение. После этого можно выполнить любое другое действие.

113. //+------------------------------------------------------------------+
114.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
115.         {
116.             string sz0;
117.             long _lparam = lparam;
118.             double _dparam = dparam;
119.             
120.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
121.             switch (id)
122.             {
123.                 case (CHARTEVENT_KEYDOWN):
124.                     if (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) break;
125.                     _lparam = (long) m_Info.ticket;
126.                     _dparam = 0;
127.                 case CHARTEVENT_CUSTOM + evMsgSetFocus:
128.                     macro_LineInFocus((m_Info.ticket == (ulong)(_lparam)) && ((EnumEvents)(_dparam) == m_Info.ev));
129.                     EventChartCustom(0, (ushort)(_dparam ? evHideMouse : evShowMouse), 0, 0, "");
130.                     m_Info.bClick = false;
131.                 case CHARTEVENT_CHART_CHANGE:
132.                     UpdateViewPort(m_Info.price);
133.                     break;
134.                 case CHARTEVENT_OBJECT_CLICK:
135.                     sz0 = GetPositionsMouse().szObjNameClick;
136.                     if (m_Info.bClick) switch (m_Info.ev)

Фрагмент из C_ElementsTrade

Предыдущий фрагмент демонстрирует решение. Я не включил весь код процедуры DispatchMessage, потому что, на мой взгляд, это совершенно не нужно, поскольку решение легко можно увидеть в этом фрагменте. Обратите внимание, что потребовалось добавить две локальные переменные, чтобы сохранить неизменными значения параметров события, полученных от MetaTrader 5. Эти переменные находятся в строках 117 и 118. Теперь возникает вполне резонный вопрос: почему бы вам не убрать квалификатор const из аргументов вызова в строке 114? Таким образом, отпала бы необходимость использовать данные переменные. Во многих случаях это было бы допустимым решением. Однако, по опыту знаю, что так делать не стоит, потому что у вас часто будет возникать соблазн изменить это значение. В какой-то момент в будущем процедура может перестать правильно интерпретировать параметры события, отправленные MetaTrader 5 или операционной системой. В Windows этот же шаблон параметров используется для обмена сообщениями между приложениями или процессами. Это чаще всего встречается при программировании DLL. По этой причине я не советую вам вносить такое изменение. Предпочтительнее всегда изменять значение локально, как показано здесь. Это избавит вас от головной боли и потерь времени на попытки понять, почему код работает неправильно.

Хорошо, после этого обратите внимание, что строка 123 определяет события клавиатуры, полученные процедурой. Существует несколько способов управления и фильтрации данных событий. В MetaTrader 5 строка 124 сравнивает полученный код клавиши с кодом ESC. Если клавиша не ESC, команда break завершает обработку события в этой строке. Например, если вы выбрали объект для перемещения и нажимаете F2, блок отмены не выполнится. Если полученная клавиша — ESC, программа выполнит строки с 125 по 133. В этом случае выполнение намеренно продолжается через два следующих блока обработки, что позволяет повторно использовать их логику без дублирования кода.


Заключительные идеи

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

Прикрепленные файлы |
Anexo.zip (779.24 KB)
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Автоматизация торговых стратегий в MQL5 (Часть 43): Адаптивная стратегия на основе канала линейной регрессии Автоматизация торговых стратегий в MQL5 (Часть 43): Адаптивная стратегия на основе канала линейной регрессии
В этой статье мы реализуем в MQL5 адаптивную систему на основе канала линейной регрессии. Она автоматически рассчитывает линию регрессии и канал стандартного отклонения за заданный пользователем период, активируется только тогда, когда абсолютное значение наклона превышает минимальный порог, что подтверждает наличие выраженного тренда, а также динамически перестраивает или продлевает канал, когда цена выходит за его границы на настраиваемую долю ширины.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
VQC-фильтр с живым обучением прямо в MQL5 поверх графового fair-value скальпера VQC-фильтр с живым обучением прямо в MQL5 поверх графового fair-value скальпера
В статье реализован двухслойный подход к входу в сделку: графовый fair-value скальпер по 28 валютным парам и вероятностный VQC‑фильтр — классически симулированная схема вращений с онлайн‑обучением в MQL5 через parameter‑shift и Adam. Разбираются кодирование признаков, аналитический градиент, согласование направлений, калибровка порога уверенности и защита от подглядывания в будущее. Читатель получает рабочую архитектуру фильтра и практические приёмы её интеграции в советник.