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

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

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

Введение

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

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

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


Модификация индикатора позиции

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "C_ElementsTrade.mqh"
05. //+------------------------------------------------------------------+
06. class C_IndicatorPosition
07. {
08.     private    :
09.         struct st00
10.         {
11.             ulong   ticket;
12.             color   corPrice, corTake, corStop;
13.             string  szShortName;
14.         }m_Infos;
15.         C_ElementsTrade *Open,
16.                         *Stop,
17.                         *Take;
18. //+------------------------------------------------------------------+
19.     public    :
20. //+------------------------------------------------------------------+
21.         C_IndicatorPosition(color corPrice, color corTake, color corStop)
22.         {
23.             ZeroMemory(m_Infos);
24.             m_Infos.corPrice = corPrice;
25.             m_Infos.corTake  = corTake;
26.             m_Infos.corStop  = corStop;
27.             Open = Take = Stop = NULL;
28.         }
29. //+------------------------------------------------------------------+
30.         ~C_IndicatorPosition()
31.         {
32.             delete Open;
33.             delete Take;
34.             delete Stop;
35.         }
36. //+------------------------------------------------------------------+
37.         bool CheckCatch(ulong ticket)
38.         {
39.             m_Infos.szShortName = StringFormat("%I64u", m_Infos.ticket = ticket);
40.             if (!PositionSelectByTicket(m_Infos.ticket)) return false;
41.             if (ObjectFind(0, m_Infos.szShortName) >= 0)
42.             {
43.                 m_Infos.ticket = 0;
44.                 return false;
45.             }
46.             IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName);
47.             EventChartCustom(0, evUpdate_Position, ticket, 0, "");
48.             
49.             return true;
50.         }
51. //+------------------------------------------------------------------+
52.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
53.         {
54.             double value;
55.             
56.             if (Open != NULL) (*Open).DispatchMessage(id, lparam, dparam, sparam);
57.             if (Take != NULL) (*Take).DispatchMessage(id, lparam, dparam, sparam);
58.             if (Stop != NULL) (*Stop).DispatchMessage(id, lparam, dparam, sparam);
59.             switch (id)
60.             {
61.                 case CHARTEVENT_CUSTOM + evUpdate_Position:
62.                     if (lparam != m_Infos.ticket) return;
63.                     if (!PositionSelectByTicket(m_Infos.ticket))
64.                     {
65.                         ChartIndicatorDelete(0, 0, m_Infos.szShortName);
66.                         return;
67.                     };
68.                     if (Open == NULL) Open = new C_ElementsTrade(m_Infos.ticket, evMsgClosePositionEA, m_Infos.corPrice, ePriorityNull, "Position opening price.");
69.                     if (Take == NULL) Take = new C_ElementsTrade(m_Infos.ticket, evMsgCloseTakeProfit, m_Infos.corTake, ePriorityOrders, "Take Profit point.");
70.                     if (Stop == NULL) Stop = new C_ElementsTrade(m_Infos.ticket, evMsgCloseStopLoss, m_Infos.corStop, (EnumPriority)(ePriorityOrders + 1), "Stop Loss point.");
71.                     (*Open).UpdatePrice(PositionGetDouble(POSITION_PRICE_OPEN));
72.                     if ((value = PositionGetDouble(POSITION_TP)) > 0) (*Take).UpdatePrice(value); else
73.                     {
74.                         delete Take;
75.                         Take = NULL;
76.                     }
77.                     if ((value = PositionGetDouble(POSITION_SL)) > 0) (*Stop).UpdatePrice(value); else
78.                     {
79.                         delete Stop;
80.                         Stop = NULL;
81.                     }
82.                     break;
83.             }
84.             ChartRedraw();
85.         }
86. //+------------------------------------------------------------------+
87. };
88. //+------------------------------------------------------------------+

C_IndicatorPosition.mqh

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

Отлично, рассмотрев приведенный выше код, вы сразу же заметите в строке 04, что мы включили новый заголовочный файл. Мы рассмотрим это ближе к концу данной статьи. Пока что не беспокойтесь об этом. Давайте сосредоточимся на этом файле. Таким образом, в строке 15 мы объявляем три указателя на класс C_ElementsTrade. Эти указатели предназначены для того, чтобы предоставить нам контролируемый доступ к коду класса CElementsTrade. 

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

Итак, обычно компилятор инициализирует переменные подходящим значением. Но, поскольку мы хотим быть уверены в этом, мы инициализируем их в конструкторе. Это происходит в строке 27, где мы указываем, что указатели будут инициализированы значением NULL. Обратите на эту деталь особое внимание, так как вам важно это знать. Также стоит отметить, что в деструкторе мы освобождаем связанную с указателями память, возвращая ее таким образом системе. Это делается между строками 32 и 34. Но в этот момент вы можете задаться вопросом: "зачем использовать команду delete, если мы ещё не выделили память с помощью команды new? Это бессмысленно". Действительно, на первый взгляд это не имеет смысла. Но вы не должны оставлять без внимания переменные, которые являются указателями. Очень часто во время выполнения программ в какой-то момент возникает ошибка. Так происходит, потому что программа выполнила какую-то операцию, связанную с указателями. И поскольку наши указатели будут объявлены в классе C_IndicatorPosition, при уничтожении объекта класса мы должны гарантировать, что память освобождается корректно.

Хорошо. Это было предварительное объяснение. Теперь давайте посмотрим, где в исходном коде были внесены дополнительные изменения. Итак, перейдем непосредственно к строке 52, где находится реализация процедуры DispatchMessage. В этот момент, если вы не будете внимательны, вы запутаетесь, так как именно здесь происходит магия. Обратите внимание, что оставшаяся часть кода осталась практически такой же, как и раньше. В этой процедуре в строке 52 есть несколько вещей, которые могут запутать вас, уважаемый читатель. Первое, что может вызвать сильное замешательство, — это код, который появляется между строками 56 и 58. Почему я делаю эти проверки перед вызовом процедуры DispatchMessage для каждого из элементов, которые мы добавим на график? Причина очень проста. При первом вызове этой процедуры в строке 52 У НАС НЕТ ЭЛЕМЕНТОВ НА ГРАФИКЕ. Поэтому, если мы не проверим, действительно ли указатели ссылаются на что-то валидное в памяти, мы рискуем выполнить "мусор". Но, поскольку MQL5, похоже, имеет какой-то механизм безопасности, при попытке выполнить "мусор" код завершится ошибкой, что приведет к аварийному удалению индикатора с графика. "Хорошо, я понял. Не придётся ли нам постоянно проводить эту проверку?" Так и есть, это необходимо делать каждый раз при выполнении процедуры, описанной в строке 52. Вы скоро поймете причину. Перейдем к следующему пункту, где мы начинаем обрабатывать событие, которое ожидает эта процедура. Обработка данного события начинается в строке 61. Теперь обратите самое пристальное внимание, потому что первое, что мы делаем, — мы проверяем в строке 62, совпадает ли значение параметра lparam с тикетом, который отслеживает индикатор. Если значения различаются, мы прерываем обработку на этом этапе. Если они одинаковы, мы проверяем в строке 63, существует ли данная позиция на сервере. Если она больше не существует, в строке 65 мы удаляем индикатор позиции с графика и в строке 66 завершаем обработку сообщения. Если эти первые проверки завершаются успешно, в строке 68 мы проверяем, создан ли уже объект элементов для цены открытия. Эта проверка будет истинной только при первом выполняемом нами вызове. Во всех остальных случаях она будет ложной. Поскольку это первый вызов, мы будем использовать оператор new для вызова конструктора. Важно, чтобы вы это понимали. Причина в том, что вы можете указать конструктору создать определенную структуру для элементов. Таким образом, используя один и тот же класс, вы сможете создать один индикатор для тейк-профита, другой для стоп-лосса и ещё один для цены открытия. Именно это мы и делаем между строками 68 и 70. Обратите внимание, что в любом случае мы инициализируем указатели, за исключением тех, которые уже были инициализированы. Мы можем дополнительно усовершенствовать данную систему, чтобы избежать ненужных вызовов. Но мы увидим это позже. Во-первых, необходимо, чтобы вы понимали, как всё работает.

После инициализации всех трех указателей в строках 68 и 70, в строке 71 мы указываем классу C_ElementsTrade, на который ссылается указатель OPEN, обновить цену. Возможно, вы думаете или представляете себе, что это глупость и что мы могли бы сделать это напрямую в конструкторе. На самом деле, мы могли бы. Конечно, при условии, что мы были уверены, что это счет типа HEDGING. Потому что, если мы используем счет типа NETTING, цена может отличаться, так как сервер сообщает нам значение, которое соответствует среднему значению позиции. Поэтому я не передаю значение напрямую в конструктор класса C_ElementsTrade. В будущем мы будем делать это по-другому. Не отчаивайтесь, уважаемый читатель. Давайте будем двигаться шаг за шагом. Поймите сначала этот более простой код, чтобы в будущем понять более сложный.

Хорошо. Теперь приближается действительно интересная часть этой процедуры DispatchMessage. То, что я объясню здесь, также применимо к тому, что будет сделано между строками 77 и 81. Это несложно. Просто будьте внимательны, и вы легко поймете, что происходит. Сначала мы получаем нужное нам значение и присваиваем его переменной value. Далее мы проверяем, больше ли это значение нуля. Если это так, значит существует линия защитного уровня, будь то тейк-профит или стоп-лосс. В любом случае, если значение существует, мы передадим его соответствующему объекту C_ElementsTrade. Другими словами, мы сделаем нечто подобное тому, что было сделано в строке 71. Однако есть некоторые нюансы относительно обновления цены открытия. Если проверяемое значение равно нулю, это означает, что соответствующий ожидаемый уровень не задан. Следовательно, элементы, представляющие цену, следует удалить из графика. Возможно, вы ожидали увидеть это здесь, в классе C_IndicatorPosition. Однако при изучении кода кажется, что в нем отсутствует место, где объекты удаляются с графика. Но да, находится здесь. Именно в строке 74 мы делаем это, так как вызываем соответствующий деструктор. Поскольку выполнение команды delete не устанавливает указатель в NULL, а только освобождает используемую память, нам необходимо четко указать указателю, что теперь он является нулевым указателем. Это делается в строке 75. Обратите внимание на это. Адрес, на который ссылается указатель, может содержать "мусор". Поскольку код должен проверять, является ли значение указателя нулевым или нет, нам необходимо явно присвоить ему нулевое значение. Не потому, что это нужно делать всегда, а потому, что работа кода не завершится, и при проверках в строках с 56 по 58 указатель может ссылаться на "мусор". Это приведет к сбою в работе кода, как только вы удалите с сервера соответствующую лимитную цену, будь то значение стоп-лосса или тейк-профита.

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


Начало класса C_ElementsTrade

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_NameHLine      m_Info.szPrefixName + "#HLINE"
05. #define def_NameBtnClose   m_Info.szPrefixName + "#CLOSE"
06. //+------------------------------------------------------------------+
07. #define def_PathBtns "Images\\Market Replay\\Orders\\"
08. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp"
09. #resource "\\" + def_Btn_Close;
10. //+------------------------------------------------------------------+
11. #include "..\Auxiliar\C_Terminal.mqh"
12. //+------------------------------------------------------------------+
13. class C_ElementsTrade : private C_Terminal
14. {
15.     private    :
16. //+------------------------------------------------------------------+
17.         struct st00
18.         {
19.             ulong      ticket;
20.             string     szPrefixName;
21.             EnumEvents ev;
22.             double     price;
23.         }m_Info;
24. //+------------------------------------------------------------------+
25.         void UpdateViewPort(void)
26.         {
27.             int x, y;
28.             
29.             ChartTimePriceToXY(0, 0, 0, m_Info.price, x, y);
30.             ObjectSetDouble(0, def_NameHLine, OBJPROP_PRICE, m_Info.price);
31.             ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, 130);
32.             ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y);
33.         }
34. //+------------------------------------------------------------------+
35.     public    :
36. //+------------------------------------------------------------------+
37.         C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, EnumPriority ePrio, string szDescr = "\n")
38.             :C_Terminal()
39.         {
40.             string szObj;
41.             
42.             ZeroMemory(m_Info);
43.             m_Info.szPrefixName = StringFormat("%I64u@%d", m_Info.ticket = ticket, (int)(m_Info.ev = ev));
44.             CreateObjectGraphics(szObj = def_NameHLine, OBJ_HLINE, _color, ePrio);
45.             ObjectSetString(0, szObj, OBJPROP_TEXT, szDescr);
46.             ObjectSetString(0, szObj, OBJPROP_TOOLTIP, szDescr);
47.             ObjectSetInteger(0, szObj, OBJPROP_SELECTABLE, ePrio != ePriorityNull);
48.             CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityOrders + 1));
49.             ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close);
50.             ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER);
51.         }
52. //+------------------------------------------------------------------+
53.         ~C_ElementsTrade()
54.         {
55.             if (m_Info.szPrefixName != "")
56.                 ObjectsDeleteAll(0, m_Info.szPrefixName);
57.         }
58. //+------------------------------------------------------------------+
59.         inline void UpdatePrice(const double price)
60.         {
61.             m_Info.price = price;
62.             UpdateViewPort();
63.         }
64. //+------------------------------------------------------------------+
65.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
66.         {            
67.             switch (id)
68.             {
69.                 case CHARTEVENT_OBJECT_CLICK:
70.                     if (sparam == def_NameBtnClose)
71.                     {
72.                         if (PositionSelectByTicket(m_Info.ticket)) switch (m_Info.ev)
73.                         {
74.                             case evMsgClosePositionEA:
75.                                 EventChartCustom(0, evMsgClosePositionEA, m_Info.ticket, 0, "");
76.                                 break;
77.                             case evMsgCloseTakeProfit:
78.                                 EventChartCustom(0, evMsgCloseTakeProfit, m_Info.ticket, PositionGetDouble(POSITION_SL), PositionGetString(POSITION_SYMBOL));
79.                                 break;
80.                             case evMsgCloseStopLoss:
81.                                 EventChartCustom(0, evMsgCloseStopLoss, m_Info.ticket, PositionGetDouble(POSITION_TP), PositionGetString(POSITION_SYMBOL));
82.                                 break;
83.                         };
84.                     }
85.                     break;
86.                 case CHARTEVENT_CHART_CHANGE:
87.                     UpdateViewPort();
88.                     break;
89.             }
90.         }
91. //+------------------------------------------------------------------+
92. };
93. //+------------------------------------------------------------------+
94. #undef def_Btn_Close
95. #undef def_PathBtns
96. //+------------------------------------------------------------------+
97. #undef def_NameBtnClose
98. #undef def_NameHLine
99. //+------------------------------------------------------------------+

C_ElementsTrade.mqh

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

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

Небольшая деталь касательно того, что я только что сказал: MetaTrader 5 — это не волшебный инструмент. При неправильном использовании он не будет работать должным образом. Необходимо понимать MetaTrader 5, чтобы по-настоящему использовать все его возможности. Таким образом, первое, что мы находим в предыдущем коде, — это именно несколько определений макросов. Обратите внимание, что в строках 04 и 05 присутствуют два определения, которые будут использоваться только в этом заголовочном файле. Я говорю "только", потому что в строках 97 и 98 мы удалили эти же определения. Если вы попытаетесь использовать их вне этого файла, компилятор выдаст ошибки, и код не скомпилируется. Хорошо, посмотрите, что мы видим в этих двух определениях. У нас есть переменная, которую мы рассмотрим позже, и строка. Только это и ничего больше. Этого более чем достаточно для того, чтобы MetaTrader 5 обрабатывал эти данные за нас, храня во внутреннем списке объекты, созданные нами в этом коде. Нам не нужен дополнительный список только для этого, пусть MetaTrader 5 сам обработает список объектов.

Строки с 07 по 09 уже обсуждались и не заслуживают особого упоминания. Затем мы можем перейти к коду класса и заметить, что здесь мало процедур и функций. Мы только начинаем создавать данный код. Давайте не будем спешить. Первое, что следует сделать при проверке класса, — это изучить процедуры его создания и уничтожения. Это конструктор и деструктор. Итак, в строке 37 мы находим объявление конструктора класса. Теперь я хочу, чтобы вы вернулись к предыдущей теме и посмотрели, что представляет собой каждый из этих параметров по отношению к тому, что было объяснено ранее. Обратите внимание, что код этого конструктора довольно прост. Всё, что мы делаем, это создаем объекты, которые будут отображаться на графике. По сути, я беру код, который ранее существовал в классе C_IndicatorPosition, и дублирую его здесь. Всё просто. В отличие от предыдущего случая, когда создавалась только одна горизонтальная линия, в данном случае у нас создано два объекта. Один из них — это линия, которая создается в строке 44 кода. Второй — кнопка взаимодействия. Она создаётся в строке 48 кода. Теперь наступает момент, когда определения в строках 04 и 05 начнут обретать смысл. Обратите внимание, что в строке 43 я определяю значение той же переменной, которая использовалась в определениях. То есть, мы используем определения, чтобы сказать MetaTrader 5: я хочу изменить что-то в определенном объекте или я хочу, чтобы ты сообщил мне, получил ли этот объект событие. MetaTrader 5 поймет наш запрос и ответит на него должным образом, выполнив именно то действие, которое требовалось. Следовательно, нам не нужен дополнительный список объектов. MetaTrader 5 возьмет на себя управление этой структурой. И это замечательно, потому что избавляет нас от значительной части работы вручную. Но прежде чем перейти к деструктору, я хочу обратить ваше внимание на одну деталь. Помните, раньше, когда мы пытались расположить кнопку взаимодействия, нам нужно было корректировать её положение, вычитая определённое значение? Что ж, как я и говорил в тот раз, у нас есть способ получше. Правильный способ — использовать строку 50. Но есть нюанс: с помощью этой строки кнопка будет позиционироваться в указанной нами точке. Мы рассмотрим этот вопрос более подробно в будущем. Пока обратите внимание, что нам больше не нужно делать ту настройку.

Хорошо. Точно так же, как конструктор отражает то, что уже существовало ранее, это применимо и к деструктору в строке 53. Я не буду это выделять, просто упоминаю об этом.

Обратите внимание, что кроме уже рассмотренных, у нас есть ещё три процедуры. Хотя их могло бы быть только две. Из практических соображений я решил разделить реализацию на три процедуры. Процедура в строке 59 является базовой, она лишь делегирует выполнение процедуре в строке 25. Данная процедура тоже довольно проста. Её единственная задача — отображать объекты на графике в надлежащих местах. Это не заслуживает отдельного упоминания, так как мы уже довольно долго говорим об этих объектах. Однако процедура в строке 65 действительно заслуживает отдельного упоминания, так как без нее ничего из того, что вы видите на графике, на самом деле не произойдет. Давайте теперь спокойно рассмотрим эту процедуру.

В этой процедуре DispatchMessage, которая является ядром данного класса, в самом начале мы обрабатываем два события. Одно из них — это щелчок по объектам, а другое — это событие обновления графика. Что ж, событие обновления, которое появляется в строке 86, является простым и понятным. Оно всего лишь позиционирует элементы или объекты на графике — то, что мы уже делали ранее. Однако обработка события клика несколько сложнее, чем в предыдущей версии. Не волнуйтесь, просто всё немного сложнее, в этом нет ничего необычного.

Обратите внимание, что при выполнении строки 69 первое, что мы делаем, — это проверяем, совпадает ли объект, переданный MetaTrader 5, с тем, который мы ожидаем обработать. То есть объект типа CLOSE. Еще раз: не пытайтесь мыслить шаблонно. Каждый экземпляр класса C_ElementsTrade будет принадлежать одному и только одному из элементов данных, которые нам потребуется отобразить. Иными словами, нам не нужно знать, соответствует ли объект CLOSE уровню тейк-профита, уровню стоп-лосса или цене открытия. Это не имеет большого значения. Главное, чтобы объект был типа CLOSE. Вот и всё; вопрос о том, к чему именно относится этот объект, решим позже.

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

Здесь мы проверим, какой именно элемент получил событие клика. Как такое возможно? Разве MetaTrader 5 не сообщит нам об этом? В некоторой степени MetaTrader 5 указывает нам на это корректно. Поскольку я хочу спокойно объяснить, как это происходит, мы используем другой подход. Таким образом, позже вы сможете понять, как работать в гораздо более продуманном ключе. В любом случае, идея та же. В текущем классе существует значение, определяемое на этапе создания объекта. То есть вернитесь к коду конструктора и обратите внимание, что в строке 43 мы сохраняем то же значение, которое будет проверяться здесь, между строками 73 и 83.

Обратите внимание на волшебство. Когда в классе C_IndicatorPosition вы указываете, что класс C_ElementsTrade должен создавать элементы или графические объекты для отображения тейк-профита, на самом деле вы устанавливаете разделение между создаваемыми наборами. Это разделение устанавливается в строке 43, однако оно используется только между строками 73 и 83.

Обратите внимание на следующее: если набор, заново созданный в классе C_IndicatorPosition, относится к стоп-лоссу, то при нажатии на кнопку взаимодействия для стоп-лосса фактически выполнится код в строке 81, отправляя сообщение советнику, чтобы сказать ему: послушай, советник, я хочу, чтобы значение стоп-лосса было удалено. Советник отправит запрос на сервер для выполнения этой задачи. Когда сервер ответит, указав, был ли запрос принят или нет, Советник отправит сообщение на график, информируя всех о том, что произошло событие. Индикатор позиции ожидает этого события. И в классе C_IndicatorPosition он удалит из памяти экземпляр C_ElementsTrade, связанный со стоп-лоссом. Таким образом, все объекты, связанные с этим экземпляром, будут удалены. Деструктор класса даст команду MetaTrader 5 удалить с графика объекты с определенным именем.

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


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

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

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

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

Файл Описание
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/13257

Прикрепленные файлы |
Anexo.zip (779.24 KB)
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Оптимизация трендовой торговли: Торговля по направлению тренда и с учётом его силы Оптимизация трендовой торговли: Торговля по направлению тренда и с учётом его силы
Это специализированный трендовый советник, который выполняет краткосрочный и долгосрочный анализ, принимает торговые решения и исполняет сделки на основе общего тренда и его силы. В статье подробно рассматривается советник, специально разработанный для терпеливых и дисциплинированных трейдеров, способных последовательно придерживаться выбранного торгового направления, открывать сделки и удерживать позиции только тогда, когда рынок движется уверенно и по тренду, не меняя часто свой рыночный уклон, особенно против тренда, пока не будут достигнуты цели по тейк-профиту.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Создание пользовательских индикаторов в MQL5 (Часть 3): Улучшение многошкальной визуализации с секторным и круговым стилями Создание пользовательских индикаторов в MQL5 (Часть 3): Улучшение многошкальной визуализации с секторным и круговым стилями
В этой статье мы расширяем индикатор в формате gauge в MQL5, добавляя поддержку нескольких осцилляторов и выбор отдельного или комбинированного отображения через перечисление. Мы вводим секторный и круглый стили шкал с помощью производных классов от базового класса шкалы, улучшая отрисовку корпуса шкалы дугами, линиями и полигонами для более аккуратного визуального представления.