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

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

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

Введение

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

В предыдущей статье, Моделирование рынка: Position View (I), мы начали обсуждать возможность создания и реализации индикатора, который позволил бы нам визуализировать открытые позиции непосредственно на графике символа. Хотя многие считают, что в этом нет необходимости, поскольку для этого мы можем рассчитывать на помощь MetaTrader 5, это неприменимо, когда мы используем операционную модель кросс-ордеров или даже, как в нашем конкретном случае, систему репликации/моделирования.

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

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


Подготовка к реализации

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

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_VERSION_DEBUG
05. //+------------------------------------------------------------------+
06. #ifdef def_VERSION_DEBUG
07.     #define macro_DEBUG_MODE(A) \
08.                     Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A));
09. #else
10.     #define macro_DEBUG_MODE(A)
11. #endif
12. //+------------------------------------------------------------------+
13. #define def_SymbolReplay           "RePlay"
14. #define def_MaxPosSlider           400
15. #define def_MaskTimeService        0xFED00000
16. #define def_IndicatorTimeFrame     (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96))))
17. #define def_IndexTimeFrame         4
18. //+------------------------------------------------------------------+
19. union uCast_Double
20. {
21.     double   dValue;
22.     long     _long;                                   // 1 Information
23.     datetime _datetime;                               // 1 Information
24.     uint     _32b[sizeof(double) / sizeof(uint)];     // 2 Informations
25.     ushort   _16b[sizeof(double) / sizeof(ushort)];   // 4 Informations
26.     uchar    _8b [sizeof(double) / sizeof(uchar)];    // 8 Informations
27. };
28. //+------------------------------------------------------------------+
29. enum EnumEvents     {
30.             evTicTac,                         //Event of tic-tac
31.             evHideMouse,                      //Hide mouse price line
32.             evShowMouse,                      //Show mouse price line
33.             evHideBarTime,                    //Hide bar time
34.             evShowBarTime,                    //Show bar time
35.             evHideDailyVar,                   //Hide daily variation
36.             evShowDailyVar,                   //Show daily variation
37.             evHidePriceVar,                   //Hide instantaneous variation
38.             evShowPriceVar,                   //Show instantaneous variation
39.             evCtrlReplayInit,                 //Initialize replay control
40.             evChartTradeBuy,                  //Market buy event
41.             evChartTradeSell,                 //Market sales event 
42.             evChartTradeCloseAll,             //Event to close positions
43.             evChartTrade_At_EA,               //Event to communication
44.             evEA_At_ChartTrade,               //Event to communication
45.             evChatWriteSocket,                //Event to Mini Chat
46.             evChatReadSocket                  //Event To Mini Chat
47.                         };
48. //+------------------------------------------------------------------+
49. enum EnumPriority {                           //Priority list on objects
50.             ePriorityNull = -1,
51.             ePriorityDefault = 0,
52.             ePriorityChartTrade = 5,
53.             ePriorityOrders = 10
54.                         };
55. //+------------------------------------------------------------------+

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

Обратите внимание на изменение, которое мы внесли в файл Defines.mqh. Оно заключается в добавлении перечисления в строке 49. Но почему? Причина проста: Как я упоминал в предыдущей статье, графические объекты могут в итоге накладываться друг на друга. Худший случай — это когда два объекта типа «линия» полностью накладываются друг на друга. В таком случае нет возможности быстро и просто выбрать один из объектов. Однако, если указать MetaTrader 5, что даже объекты одного типа будут иметь разный приоритет, MetaTrader 5 учтет это при выборе перекрывающихся объектов и отдаст приоритет объекту с более высоким значением. Тем не менее, будьте осторожны, делая так. Я не хочу, чтобы у вас вошло в привычку присваивать всё более и более высокие значения для приоритета своих объектов в ущерб другим.

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

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

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

201. //+------------------------------------------------------------------+
202. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const EnumPriority zOrder = ePriorityNull) const
203.             {
204.                 ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false);
205.                  ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0);
206. 

Фрагмент из класса C_Terminal

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

164. //+------------------------------------------------------------------+
165.         template <typename T >
166.         void CreateObjectEditable(eObjectsIDE arg, T value)
167.             {
168.                 DeleteObjectEdit();
169.                 CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, ePriorityChartTrade);
170.                 ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3);
171.                 ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3);

Фрагмент из класса C_ChartFloatingRAD

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


Приступаем к реализации новых объектов

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Indicator for tracking an open position on the server."
04. #property version   "1.00"
05. #property indicator_chart_window
06. #property indicator_plots 0
07. //+------------------------------------------------------------------+
08. #define def_SufixLinePrice   "Price"
09. #define def_SufixLineTake    "Take"
10. #define def_SufixLineStop    "Stop"
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Auxiliar\C_Terminal.mqh>
13. //+------------------------------------------------------------------+
14. input color user00 = clrBlue;               //Color Line Price
15. input color user01 = clrForestGreen;        //Color Line Take Profit
16. input color user02 = clrFireBrick;          //Color Line Stop Loss
17. //+------------------------------------------------------------------+
18. C_Terminal *Terminal;
19. struct st
20. {
21.     long       id;
22.     string     szPrefixName;
23. }glVariables;
24. //+------------------------------------------------------------------+
25. void CreateLinePriceOpen(const string szObjName)
26. {
27.     (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull);
28.     ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN));
29. }
30. //+------------------------------------------------------------------+
31. int OnInit()
32. {
33.     ZeroMemory(glVariables);
34.     Terminal = new C_Terminal();
35.     if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED;
36.     glVariables.id = (*Terminal).GetInfoTerminal().ID;
37.     glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET));
38.     CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice);
39.     
40.     return INIT_SUCCEEDED;
41. }
42. //+------------------------------------------------------------------+
43. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
44. {
45.     return rates_total;
46. }
47. //+------------------------------------------------------------------+
48. void OnDeinit(const int reason)
49. {
50.     delete Terminal;
51.     if (glVariables.id > 0)
52.         ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName);
53. }
54. //+------------------------------------------------------------------+

Исходный код индикатора позиции

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

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

Давайте пока пропустим процедуру в строке 25 и перейдем к строке 31. Там индикатор начнет работать в MetaTrader 5. Обратите внимание, что первым делом мы обнуляем память структуры glVariables. Зачем это делать? Причина, как мы уже говорили, заключается в том, что эта структура позже исчезнет. Но даже если это останется неизменным, нужно убедиться, что все переменные, присутствующие в структуре, имеют нулевое начальное значение. Делать это с помощью показанного вызова проще, чем объявлять переменную за переменной. И если я вдруг забуду какую-то из них, она совершенно точно останется с нулевым значением.

Хорошо. Строки 34 и 35 уже объяснялась в предыдущей статье, поэтому мы можем их пропустить. Строка 36, с другой стороны, служит лишь для сохранения значения, которое сохраняется в классе C_Terminal, для дальнейшего использования. На этом мы подходим к главным строкам. В первой строке, то есть в строке 37, мы определяем значение, которое будет префиксом имени создаваемого объекта, в данном случае, объекта типа HLINE. Значение этого префикса — это как раз то самое числовое значение, которое определяет тикет позиции. Это гарантирует, что на графике будет только один объект с таким именем.

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

Тогда давайте перейдем к процедуре в строке 25, чтобы понять некоторые вещи. Потому что на данном этапе код всё ещё довольно прост и понятен. Эта процедура получает имя, которое будет присвоено объекту типа HLINE. Но где мы создаём этот объект? Мы делаем это с помощью класса C_Terminal, а именно в строке 27. А теперь обратите пристальное внимание на следующее: в четвертом параметре строки 27 мы используем очень низкий приоритет, фактически, самый низкий в списке. Почему?

Можно подумать: "но разве эта линия, которую мы создаём в данный момент, не является частью индикатора позиции и ордеров? Почему этому объекту присваивается более низкий приоритет обработки, чем другим объектам? Это бессмысленно. Приоритет, по моему мнению, должен быть тем, который мы внесли в список как приоритет объектов, создаваемых в индикаторе позиций и ордеров". Нет, уважаемый читатель, так думать, особенно в этом конкретном случае, неправильно, и причина проста. Данная горизонтальная линия, и пусть это будет предельно ясно, только эта линия, должна иметь самый низкий приоритет из всех возможных. И причина проста: она не будет перемещаться и не должна получать события, поскольку представляет собой цену или точку, в которой торговый сервер указывает открытую позицию. Теперь вы понимаете, почему ему присвоен такой приоритет?

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Indicator for tracking an open position on the server."
04. #property version   "1.00"
05. #property indicator_chart_window
06. #property indicator_plots 0
07. //+------------------------------------------------------------------+
08. #define def_SufixLinePrice   "Price"
09. #define def_SufixLineTake    "Take"
10. #define def_SufixLineStop    "Stop"
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Auxiliar\C_Terminal.mqh>
13. //+------------------------------------------------------------------+
14. input color user00 = clrBlue;               //Color Line Price
15. input color user01 = clrForestGreen;        //Color Line Take Profit
16. input color user02 = clrFireBrick;          //Color Line Stop Loss
17. //+------------------------------------------------------------------+
18. C_Terminal *Terminal;
19. struct st
20. {
21.     long       id;
22.     string     szPrefixName;
23. }glVariables;
24. //+------------------------------------------------------------------+
25. void CreateLinePriceOpen(const string szObjName)
26. {
27.     (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull);
28.     ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN));
29. }
30. //+------------------------------------------------------------------+
31. void CreateLineStopAndTake(const string szObjName, const double price, const color cor)
32. {
33.     if (price <= 0) return;
34.     (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, ePriorityOrders);
35.     ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price);
36. }
37. //+------------------------------------------------------------------+
38. int OnInit()
39. {
40.     ZeroMemory(glVariables);
41.     Terminal = new C_Terminal();
42.     if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED;
43.     glVariables.id = (*Terminal).GetInfoTerminal().ID;
44.     glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET));
45.     CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice);
46.     CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01);
47.     CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02);
48.     
49.     return INIT_SUCCEEDED;
50. }
51. //+------------------------------------------------------------------+
52. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
53. {
54.     return rates_total;
55. }
56. //+------------------------------------------------------------------+
57. void OnDeinit(const int reason)
58. {
59.     delete Terminal;
60.     if (glVariables.id > 0)
61.         ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName);
62. }
63. //+------------------------------------------------------------------+

Исходный код индикатора позиции

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

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

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

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

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


Продолжаем улучшать код

Один из способов, который мы можем добавить, чтобы пользователь понимал, что представляет собой тот или иной объект и зачем он нужен, — это описание объекта. Сделать это очень просто. Просто заменим приведенный выше код тем кодом, который показан ниже:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Indicator for tracking an open position on the server."
04. #property version   "1.00"
05. #property indicator_chart_window
06. #property indicator_plots 0
07. //+------------------------------------------------------------------+
08. #define def_SufixLinePrice   "Price"
09. #define def_SufixLineTake    "Take"
10. #define def_SufixLineStop    "Stop"
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Auxiliar\C_Terminal.mqh>
13. //+------------------------------------------------------------------+
14. input color user00 = clrRoyalBlue;          //Color Line Price
15. input color user01 = clrForestGreen;        //Color Line Take Profit
16. input color user02 = clrFireBrick;          //Color Line Stop Loss
17. //+------------------------------------------------------------------+
18. C_Terminal *Terminal;
19. struct st
20. {
21.     long       id;
22.     string     szPrefixName;
23. }glVariables;
24. //+------------------------------------------------------------------+
25. void CreateLinePriceOpen(const string szObjName, const string szDescription = "\n")
26. {
27.     (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull);
28.     ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN));
29.     ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription);
30.     ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription);
31. }
32. //+------------------------------------------------------------------+
33. void CreateLineStopAndTake(const string szObjName, const double price, const color cor, const string szDescription = "\n")
34. {
35.     if (price <= 0) return;
36.     (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, ePriorityOrders);
37.     ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price);
38.     ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription);
39.     ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription);
40. }
41. //+------------------------------------------------------------------+
42. int OnInit()
43. {
44.     ZeroMemory(glVariables);
45.     Terminal = new C_Terminal();    
46.     if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED;
47.     glVariables.id = (*Terminal).GetInfoTerminal().ID;
48.     glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET));
49.     CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice, "Position opening price.");
50.     CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point.");
51.     CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point.");
52.     
53.     return INIT_SUCCEEDED;
54. }
55. //+------------------------------------------------------------------+
56. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
57. {
58.     return rates_total;
59. }
60. //+------------------------------------------------------------------+
61. void OnDeinit(const int reason)
62. {
63.     delete Terminal;
64.     if (glVariables.id > 0)
65.         ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName);
66. }
67. //+------------------------------------------------------------------+

Исходный код индикатора позиции

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

А также в списке объектов, где теперь появляется следующее:

Запомните: чтобы текст отображался на графике, как показано на изображении выше, необходимо, чтобы в настройках графика был отмечен следующий параметр:

Это свойство можно также включить из кода MQL5. Для этого достаточно добавить в программу всего одну строку кода:

41. //+------------------------------------------------------------------+
42. int OnInit()
43. {
44.     ZeroMemory(glVariables);
45.     Terminal = new C_Terminal();
46.     if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED;
47.     glVariables.id = (*Terminal).GetInfoTerminal().ID;
48.     ChartSetInteger(glVariables.id, CHART_SHOW_OBJECT_DESCR, true);    
49.     glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET));
50.     CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice, "Position opening price.");
51.     CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point.");
52.     CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point.");
53.     
54.     return INIT_SUCCEEDED;
55. }
56. //+------------------------------------------------------------------+

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

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

Мы также можем изменить это свойство с помощью кода MQL5. Но опять же, я не хочу беспокоиться или менять настройки, к которым пользователь уже привык. Я хочу приложить как можно меньше усилий для достижения поставленной цели. Но, возвращаясь к основному коду, нужно обратить внимание, уважаемый читатель, на кое-что: как в объявлении процедуры CreateLinePriceOpen, так и в объявлении процедуры CreateLineStopAndTake значению szDescription присвоено определенное значение. Если не указать описание для объекта, он получит описание, значение которого будет "\n". Это указано в документации MQL5, где говорится следующее:

Текст "tooltip" (всплывающей подсказки). Если свойство не определено, то будет отображена всплывающая подсказка, автоматически сгенерированная терминалом. Всплывающую подсказку можно отключить, присвоив ей значение "\n" (перенос строки).

Таким образом, это значение по умолчанию, которое получает szDescription, фактически не повлияет на информацию, которая разместится в свойстве OBJPROP_TEXT в строках 29 и 38. Однако это помешает терминалу MetaTrader 5 сгенерировать значение для свойства OBJPROP_TOOLTIP, объявленного в строках 30 и 39.

"Как мы получим доступ к свойству OBJPROP_TOOLTIP? Разве это не то же самое, что OBJPROP_TEXT?" Нет, это не одно и то же. Свойство OBJPROP_TEXT — это именно то, что мы видим, когда объект позволяет добавлять к нему текст. Одним из объектов, позволяющих это сделать, является горизонтальная линия. Однако это свойство присутствует не у всех объектов.

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

Если раньше вы это не замечали, то на анимации ниже показано, для чего предназначено это маленькое окошко, а также как просмотреть содержимое свойства OBJPROP_TOOLTIP.

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Indicator for tracking an open position on the server."
04. #property version   "1.00"
05. #property indicator_chart_window
06. #property indicator_plots 0
07. //+------------------------------------------------------------------+
08. #define def_SufixLinePrice   "Price"
09. #define def_SufixLineTake    "Take"
10. #define def_SufixLineStop    "Stop"
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Auxiliar\C_Terminal.mqh>
13. //+------------------------------------------------------------------+
14. input color user00 = clrRoyalBlue;          //Color Line Price
15. input color user01 = clrForestGreen;        //Color Line Take Profit
16. input color user02 = clrFireBrick;          //Color Line Stop Loss
17. //+------------------------------------------------------------------+
18. C_Terminal *Terminal;
19. struct st
20. {
21.     long       id;
22.     string     szPrefixName;
23. }glVariables;
24. //+------------------------------------------------------------------+
25. void CreateLineInfos(const string szObjName, const double price, const color cor, const string szDescription = "\n")
26. {
27.     if (price <= 0) return;
28.     (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == user00 ? ePriorityNull : ePriorityOrders))
29.     ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price);
30.     ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription);
31.     ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription);
32.     ObjectSetInteger(glVariables.id, szObjName, OBJPROP_SELECTABLE, cor != user00);
33. }
34. //+------------------------------------------------------------------+
35. int OnInit()
36. {
37.     ZeroMemory(glVariables);
38.     Terminal = new C_Terminal();
39.     if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED;
40.     glVariables.id = (*Terminal).GetInfoTerminal().ID;
41.     glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET));
42.     CreateLineInfos(glVariables.szPrefixName + def_SufixLinePrice, PositionGetDouble(POSITION_PRICE_OPEN), user00, "Position opening price.");
43.     CreateLineInfos(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point.");
44.     CreateLineInfos(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point.");
45.     
46.     return INIT_SUCCEEDED;
47. }
48. //+------------------------------------------------------------------+
49. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
50. {
51.     return rates_total;
52. }
53. //+------------------------------------------------------------------+
54. void OnDeinit(const int reason)
55. {
56.     delete Terminal;
57.     if (glVariables.id > 0)
58.         ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName);
59. }
60. //+------------------------------------------------------------------+

Исходный код индикатора позиции

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


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

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

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

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

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

Прикрепленные файлы |
Anexo.zip (779.24 KB)
Алгоритм оптимизации койотов — Coyote Optimization Algorithm (COA) Алгоритм оптимизации койотов — Coyote Optimization Algorithm (COA)
Представляем MQL5-реализацию Coyote Optimization Algorithm: стаи с локальными альфами, медианная тенденция и встроенный кроссовер обеспечивают параллельное исследование областей пространства и контроль преждевременной сходимости. Алгоритм встроен в C_AO и проверен на стандартном стенде и композитном античит-тесте. В статье — код, псевдокод и разбор операторов, позволяющие применить COA для оптимизации параметров торговой системы.
Изучение стандартной библиотеки MQL5 (часть 1): Знакомство с CTrade, CiMA и CiATR Изучение стандартной библиотеки MQL5 (часть 1): Знакомство с CTrade, CiMA и CiATR
Стандартная библиотека MQL5 — чрезвычайно полезный инструмент при разработке торговых алгоритмов для MetaTrader 5. В этой серии мы будем учиться создавать с помощью нее эффективные торговые инструменты для MetaTrader 5. Под инструментами подразумеваются собственные советники, индикаторы и другие вспомогательные средства. Сегодня мы разработаем трендового советника с использованием классов CTrade, CiMA и CiATR. Тема будет полезна всем — и начинающим, так и опытным разработчикам. Приятного чтения.
Разработка инструментария для анализа Price Action (Часть 65): Создание системы для мониторинга и анализа построенных вручную уровней Фибоначчи Разработка инструментария для анализа Price Action (Часть 65): Создание системы для мониторинга и анализа построенных вручную уровней Фибоначчи
Инструмент коррекции Фибоначчи – важный элемент анализа Price Action, указывающий ключевые уровни возможной рыночной реакции. Однако его эффективность часто ограничена необходимостью постоянного ручного наблюдения, из-за чего часть сетапов может быть пропущена. В этой части серии представлен инструмент, который с помощью MQL5 синхронизирует и активно отслеживает вручную построенные уровни Фибоначчи, сочетая дискреционный подход с автоматизированным контролем.
Разработка торговой стратегии с использованием подхода Volume Boundary Разработка торговой стратегии с использованием подхода Volume Boundary
В мире технического анализа цена часто оказывается в центре внимания. Трейдеры тщательно размечают поддержку, сопротивление и паттерны, но нередко игнорируют ключевую силу, которая движет этими ценовыми движениями: объем. В этой статье рассматривается новый подход к анализу объема — индикатор Volume Boundary. Такое преобразование с использованием сложных сглаживающих функций, таких как кривая «бабочка» и тройная синусоида, облегчает интерпретацию данных и позволяет разрабатывать системные торговые стратегии.