Español Português
preview
От начального до среднего уровня: Объекты (IV)

От начального до среднего уровня: Объекты (IV)

MetaTrader 5Примеры |
74 0
CODE X
CODE X

Введение

В предыдущей статье От начального до среднего уровня: Объекты (III) мы показали, как можно реализовать индикатор с очень простой целью: легко разместить линию тренда на графике, не открывая меню MetaTrader 5.

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

Увиденное и сделанное в предыдущей статье, — это лишь малая часть того, чего мы действительно можем достичь, когда начнем программировать собственные приложения с конкретными целями. В этой статье мы постараемся показать нечто очень интересное: индикатор, скрытый в MetaTrader 5, о существовании которого практически никто не знает.

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


Скрытый индикатор в MetaTrader 5

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

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

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. #define macro_NameObject  def_Prefix + (string)(ObjectsTotal(0) + 1)
07. //+------------------------------------------------------------------+
08. #include <Tutorial\File 01.mqh>
09. //+------------------------------------------------------------------+
10. st_Cross gl_Cross;
11. //+------------------------------------------------------------------+
12. int OnInit()
13. {
14.     gl_Cross.Init();
15. 
16.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
17.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
18.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
19. 
20.     return INIT_SUCCEEDED;
21. };
22. //+------------------------------------------------------------------+
23. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
24. {
25.     return rates_total;
26. };
27. //+------------------------------------------------------------------+
28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
29. {
30.     st_TimePrice tp;
31.     static string isPaint = "";
32. 
33.     switch (id)
34.     {
35.         case CHARTEVENT_KEYDOWN:
36.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) gl_Cross.Hide();
37.             break;
38.         case CHARTEVENT_MOUSE_MOVE:
39.             if (((uchar)sparam & MOUSE_MIDDLE) != 0)
40.             {
41.                 tp = gl_Cross.Move((ushort)lparam, (ushort)dparam);
42.                 if (isPaint == "")
43.                 {
44.                     ObjectCreate(0, isPaint = macro_NameObject, OBJ_TREND, 0, tp.Time, tp.Price);
45.                     ObjectSetInteger(0, isPaint, OBJPROP_SELECTABLE, true);
46.                     ObjectSetInteger(0, isPaint, OBJPROP_COLOR, clrMagenta);
47.                     ObjectSetInteger(0, isPaint, OBJPROP_WIDTH, 3);
48.                     ObjectSetInteger(0, isPaint, OBJPROP_RAY_RIGHT, true);
49.                 }
50.                 ObjectMove(0, isPaint, 1, tp.Time, tp.Price);
51.                 gl_Cross.Show();
52.             }else
53.             {
54.                 isPaint = "";
55.                 gl_Cross.Hide();
56.             }
57.             break;
58.         case CHARTEVENT_MOUSE_WHEEL:
59.             break;
60.     }
61.     ChartRedraw();
62. };
63. //+------------------------------------------------------------------+
64. void OnDeinit(const int reason)
65. {
66.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
67.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
68. 
69.     gl_Cross.Hide();
70. 
71.     if (reason == REASON_REMOVE)
72.         ObjectsDeleteAll(0, def_Prefix);
73. };
74. //+------------------------------------------------------------------+

Код 01

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

В данный момент мы хотим заменить объект OBJ_TREND объектом OBJ_FIBO. С помощью этой простой замены мы получаем следующее:

Анимация 01

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

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #define def_Prefix  "Demo"
005. //+------------------------------------------------------------------+
006. #define macro_NameObject  def_Prefix + (string)(ObjectsTotal(0) + 1)
007. //+------------------------------------------------------------------+
008. #include <Tutorial\File 01.mqh>
009. //+------------------------------------------------------------------+
010. st_Cross gl_Cross;
011. //+------------------------------------------------------------------+
012. int OnInit()
013. {
014.     gl_Cross.Init();
015. 
016.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
017.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
018.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
019. 
020.     return INIT_SUCCEEDED;
021. };
022. //+------------------------------------------------------------------+
023. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
024. {
025.     return rates_total;
026. };
027. //+------------------------------------------------------------------+
028. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
029. {
030.     st_TimePrice tp;
031.     static string isPaint = "";
032. 
033.     switch (id)
034.     {
035.         case CHARTEVENT_KEYDOWN:
036.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) gl_Cross.Hide();
037.             break;
038.         case CHARTEVENT_MOUSE_MOVE:
039.             if (((uchar)sparam & MOUSE_MIDDLE) != 0)
040.             {
041.                 tp = gl_Cross.Move((ushort)lparam, (ushort)dparam);
042.                 if (isPaint == "")
043.                 {
044.                     ObjectCreate(0, isPaint = macro_NameObject, OBJ_FIBO, 0, tp.Time, tp.Price);
045.                     Modifier_OBJ_FIBO(isPaint);
046.                 }
047.                 ObjectMove(0, isPaint, 1, tp.Time, tp.Price);
048.                 gl_Cross.Show();
049.             }else
050.             {
051.                 isPaint = "";
052.                 gl_Cross.Hide();
053.             }
054.             break;
055.         case CHARTEVENT_MOUSE_WHEEL:
056.             break;
057.     }
058.     ChartRedraw();
059. };
060. //+------------------------------------------------------------------+
061. void OnDeinit(const int reason)
062. {
063.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
064.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
065. 
066.     gl_Cross.Hide();
067. 
068.     if (reason == REASON_REMOVE)
069.         ObjectsDeleteAll(0, def_Prefix);
070. };
071. //+------------------------------------------------------------------+
072. void Modifier_OBJ_FIBO(const string szNameObj)
073. {
074.     const double nLevels[] = 
075.     {
076.         0,
077.         1,
078.         2.5
079.     };
080.     const string sLevels[] =
081.     {
082.         "Stop",
083.         "Enter",
084.         "Take"
085.     };
086.     const color cLevels[] =
087.     {
088.         clrRed,
089.         clrBlue,
090.         clrGreen
091.     };
092.     
093.     ObjectSetInteger(0, szNameObj, OBJPROP_SELECTABLE, false);
094.     ObjectSetInteger(0, szNameObj, OBJPROP_COLOR, clrNONE);
095.     ObjectSetInteger(0, szNameObj, OBJPROP_WIDTH, 3);
096.     ObjectSetInteger(0, szNameObj, OBJPROP_RAY_RIGHT, true);
097. 
098.     ObjectSetInteger(0, szNameObj, OBJPROP_LEVELS, nLevels.Size());
099.     for (uint c = 0; c < nLevels.Size(); c++)
100.     {
101.         ObjectSetDouble(0, szNameObj, OBJPROP_LEVELVALUE, c, nLevels[c]);
102.         ObjectSetInteger(0, szNameObj, OBJPROP_LEVELCOLOR, c, cLevels[c]);
103.         ObjectSetInteger(0, szNameObj, OBJPROP_LEVELWIDTH, c, 2);
104.         ObjectSetString(0, szNameObj, OBJPROP_LEVELTEXT, c, sLevels[c]);
105.     }
106. }
107. //+------------------------------------------------------------------+

Код 02

Код 02 очень интересен, особенно тем, что в нем почти полностью реализован тот объект, который я хочу показать. Как это возможно? Уважаемый читатель, если вы внимательно посмотрите, то увидите, что в строке 45 мы вызываем процедуру, которая находится в строке 72. Данная процедура, скажем так, выглядит несколько неаккуратно, потому что она гораздо более неорганизованна, чем мне хотелось бы изначально. Тем не менее, я думаю, так будет проще понять ее функционирование.

Прежде чем объяснить, как работает процедура, давайте посмотрим на результат, как показано в анимации ниже:

Анимация 02

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

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

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

Хорошо, но как же удалось создать данный инструмент? И, кроме того, как мы можем улучшить его или адаптировать к нашему стилю работы? Чтобы понять это, давайте теперь сосредоточимся на процедуре Modifier_OBJ_FIBO, которая начинается в строке 72 кода 02.

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

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

Строка 94 не позволяет пунктирной линии быть видимой на графике. Данная линия появляется в анимации 01 в пурпурном цвете, но когда ей присваивается цвет clrNONE, она перестает быть видимой, хотя объектная линия остается созданной. Следующие две строки, 95 и 96, изменяют только свойства объекта OBJ_FIBO, здесь больше нечего отметить. Теперь мы переходим к той части, которая нас действительно интересует, а именно к строке 98.

Объект OBJ_FIBO состоит из уровней. Неважно, какой бы вариант объекта Фибоначчи мы ни использовали: во всех случаях он создается из них. Поскольку MetaTrader 5 не знает, сколько уровней следует создать, мы используем строку 98, чтобы указать ему это. В этот момент платформа будет знать, сколько уровней должно быть. Можно указать больше или меньше уровней; однако, поскольку здесь мы ищем модификацию, показанную в анимации 02, мы будем использовать несколько уровней, а именно три.

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

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

Самая сложная часть этого массива — это именно его значения. "Зачем использовать именно данные значения? А нельзя ли использовать другие варианты?" Чтобы это понять, сначала нужно узнать ещё один аспект числа Фибоначчи. В объекте Фибоначчи мы работаем со значениями от нуля до единицы; всё, что находится в этом интервале, отображается внутри стандартной сетки Фибоначчи, знакомой всем. Однако можно присваивать значения меньше нуля и больше единицы, и при этом мы создаём расширенную проекцию самого объекта. Обратите внимание на следующее: когда мы начинаем рисовать объект OBJ_FIBO на диаграмме, точка начала обводки соответствует значению один, а при перетаскивании объекта для его создания мы получаем позицию значения ноль.

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

Данным образом мы рассчитываем проекцию, в которой будет находиться цель возможной сделки. Если хотите использовать целевое соотношение 1:1, следует указать значение два в строке 78; таким образом, то же расстояние, которое существует между точкой входа и точкой стопа, будет спроецировано в качестве целевого расстояния операции.

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

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

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

                   .
                   .
                   .
71. //+------------------------------------------------------------------+
72. void Modifier_OBJ_FIBO(const string szNameObj)
73. {
74. #define macro_Mod_OBJ_FIBO(txt, pos, cor, width, style) {                       \
75.             ObjectSetDouble(0, szNameObj, OBJPROP_LEVELVALUE, levels, pos);     \
76.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELCOLOR, levels, cor);    \
77.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELWIDTH, levels, width);  \
78.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELSTYLE, levels, style);  \
79.             ObjectSetString(0, szNameObj, OBJPROP_LEVELTEXT, levels, txt);      \
80.             levels++;                                                           \
81.                                                         }
82. 
83.     int levels = 0;
84. 
85.     ObjectSetInteger(0, szNameObj, OBJPROP_SELECTABLE, false);
86.     ObjectSetInteger(0, szNameObj, OBJPROP_COLOR, clrNONE);
87.     ObjectSetInteger(0, szNameObj, OBJPROP_RAY_RIGHT, true);
88. 
89.     macro_Mod_OBJ_FIBO("Stop", 0, clrRed, 2, STYLE_SOLID);
90.     macro_Mod_OBJ_FIBO("Enter", 1, clrBlue, 2, STYLE_DASH);
91.     macro_Mod_OBJ_FIBO("Partial", 1.5, clrYellowGreen, 1, STYLE_DASHDOTDOT);
92.     macro_Mod_OBJ_FIBO("Take", 2, clrGreen, 2, STYLE_SOLID);
93.     ObjectSetInteger(0, szNameObj, OBJPROP_LEVELS, levels);
94. 
95. #undef macro_Mod_OBJ_FIBO
96. }
97. //+------------------------------------------------------------------+

Код 03

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

Изображение 01

Прошу заметить, что то, что мы делаем (и это видно на изображении 01) невозможно достичь, манипулируя объектом Фибоначчи непосредственно на графике. Как бы мы ни старались, нам не удастся создать нечто подобное тому, что показано на изображении 01; это возможно только с помощью кода. В данном случае соотношение между точкой стопа и целью составляет 1:1, при этом частичный выход осуществляется на 50% дистанции.

Самая сложная часть — это именно сочетание цветов и линий. Если вы не верите, попробуйте воспроизвести это с помощью объекта Фибоначчи, вставленного из меню MetaTrader 5, и вы увидите, что это невозможно.

В данном сценарии из кода 03, процедуру Modifier_OBJ_FIBO, на мой взгляд, гораздо проще модифицировать и настраивать, поскольку мы используем макрос, который позволяет очень легко создавать уровни. Любой новый уровень можно добавить, разместив его перед строкой 93, чтобы MetaTrader 5 знал, сколько и какие уровни нужно отобразить на графике.

"То, что вы показали, довольно интересно, но у меня есть вопрос. Я понимаю, что цель этих статей не в том, чтобы научить создавать полноценное приложение, однако, если бы мы хотели, чтобы пользователь взаимодействовал с этим последним индикатором, как бы мы могли это обеспечить? Я спрашиваю это, потому что после компиляции установленные значения больше нельзя изменить. Как мы могли хотя бы скорректировать взаимосвязь между стопом и целью?" С объяснением проблем нет; у нас есть время, чтобы посмотреть, как это можно сделать. Чтобы разграничить темы, давайте рассмотрим это подробнее.


Регулировка соотношения стоп-цель

Существует несколько способов позволить пользователю указать соотношение между стопом и целью. Один из простых способов — использовать значение типа double, как показано ниже:

                   .
                   .
                   .
09. //+------------------------------------------------------------------+
10. input double user01 = 1.5;                  //Stop-Target Relationship
11. //+------------------------------------------------------------------+
                   .
                   .
                   .
73. //+------------------------------------------------------------------+
74. void Modifier_OBJ_FIBO(const string szNameObj)
75. {
76. #define macro_Mod_OBJ_FIBO(txt, pos, cor, width, style) {                       \
77.             ObjectSetDouble(0, szNameObj, OBJPROP_LEVELVALUE, levels, pos);     \
78.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELCOLOR, levels, cor);    \
79.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELWIDTH, levels, width);  \
80.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELSTYLE, levels, style);  \
81.             ObjectSetString(0, szNameObj, OBJPROP_LEVELTEXT, levels, txt);      \
82.             levels++;                                                           \
83.                                                         }
84. 
85.     int levels = 0;
86. 
87.     ObjectSetInteger(0, szNameObj, OBJPROP_SELECTABLE, false);
88.     ObjectSetInteger(0, szNameObj, OBJPROP_COLOR, clrNONE);
89.     ObjectSetInteger(0, szNameObj, OBJPROP_RAY_RIGHT, true);
90. 
91.     macro_Mod_OBJ_FIBO("Stop", 0, clrRed, 2, STYLE_SOLID);
92.     macro_Mod_OBJ_FIBO("Enter", 1, clrBlue, 2, STYLE_DASH);
93.     macro_Mod_OBJ_FIBO("Partial", 1 + (user01 / 2), clrYellowGreen, 1, STYLE_DASHDOTDOT);
94.     macro_Mod_OBJ_FIBO("Take", 1 + user01, clrGreen, 2, STYLE_SOLID);
95.     ObjectSetInteger(0, szNameObj, OBJPROP_LEVELS, levels);
96. 
97. #undef macro_Mod_OBJ_FIBO
98. }
99. //+------------------------------------------------------------------+

Код 04

Этот фрагмент кода 04 представляет собой простейший способ установить прямую связь между стопом и ее целью. Всё, что требовалось, — это добавить строку 10 к фрагменту и скорректировать строки 93 и 94 для генерации взаимосвязи. При выполнении кода 04 мы получаем результат, показанный на следующем изображении:

Изображение 02

После корректировки соотношения, показанного на изображении 02, можно использовать его непосредственно на графике, и результат будет аналогичен тому, который видно на изображении ниже:

Изображение 03

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

Изображение 04

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

Изображение 05

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

Единственным недостатком может быть то, что линии пересекаются, но это легко решается, если предоставить пользователю возможность нарисовать (или нет) продолжение линии справа. Для этого достаточно изменить код, как показано ниже:

                   .
                   .
                   .
09. //+------------------------------------------------------------------+
10. input double    user01 = 1.5;                   //Stop-Target Relationship
11. input bool      user02 = true;                  //Extend lines to the right
12. //+------------------------------------------------------------------+
                   .
                   .
                   .
73. //+------------------------------------------------------------------+
74. void Modifier_OBJ_FIBO(const string szNameObj)
75. {
76. #define macro_Mod_OBJ_FIBO(txt, pos, cor, width, style) {                       \
77.             ObjectSetDouble(0, szNameObj, OBJPROP_LEVELVALUE, levels, pos);     \
78.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELCOLOR, levels, cor);    \
79.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELWIDTH, levels, width);  \
80.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELSTYLE, levels, style);  \
81.             ObjectSetString(0, szNameObj, OBJPROP_LEVELTEXT, levels, txt);      \
82.             levels++;                                                           \
83.                                                         }
84. 
85.     int levels = 0;
86. 
87.     ObjectSetInteger(0, szNameObj, OBJPROP_SELECTABLE, false);
88.     ObjectSetInteger(0, szNameObj, OBJPROP_COLOR, clrNONE);
89.     ObjectSetInteger(0, szNameObj, OBJPROP_RAY_RIGHT, user02);
90. 
91.     macro_Mod_OBJ_FIBO("Stop", 0, clrRed, 2, STYLE_SOLID);
92.     macro_Mod_OBJ_FIBO("Enter", 1, clrBlue, 2, STYLE_DASH);
93.     macro_Mod_OBJ_FIBO("Partial", 1 + (user01 / 2), clrYellowGreen, 1, STYLE_DASHDOTDOT);
94.     macro_Mod_OBJ_FIBO("Take", 1 + user01, clrGreen, 2, STYLE_SOLID);
95.     ObjectSetInteger(0, szNameObj, OBJPROP_LEVELS, levels);
96. 
97. #undef macro_Mod_OBJ_FIBO
98. }
99. //+------------------------------------------------------------------+

Код 05

Опять же, это простое изменение во фрагменте кода 05; в данном случае мы добавляем строку 11. Её значение используется в строке 89 для контроля того, будет ли линия продлена до крайнего правого края. На следующем изображении показана настройка, при которой линия не продолжается.

Изображение 06

В результате на графике мы получаем нечто подобное этому:

Изображение 07

Прошу заметить: мы добавляем новый анализ на изображении 05, и хотя мы изменили настройки индикатора (изображение 06), не удаляя его с графика, это не удаляет и не влияет на то, что уже существовало. Приложив совсем немного усилий, мы создали нечто действительно интересное.

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

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


Используем левую кнопку мыши для создания рисунка

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

При нажатии средней кнопки мыши все события мыши перенаправляются на строку 38, как показано в коде 02. Пока кнопка нажата, перекрестие анализа остается видимым, что позволяет выполнить построение рисунка. Когда центральная кнопка отпускается, строка 52 из кода 02 удаляет перекрестие, указывая на то, что мы больше не можем взаимодействовать с системой рисования.

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

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

                   .
                   .
                   .
029. //+------------------------------------------------------------------+
030. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
031. {
032. #define macro_CLEAN_EVENT   {                               \
033.             isPaint = "";                                   \
034.             gl_Cross.Hide();                                \
035.             bMouseL = false;                                \
036.                             }
037. 
038.     st_TimePrice    tp;
039.     static string   isPaint = "";
040.     static bool     bMouseL = false;
041. 
042.     switch (id)
043.     {
044.         case CHARTEVENT_KEYDOWN:
045.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) macro_CLEAN_EVENT;
046.             break;
047.         case CHARTEVENT_MOUSE_MOVE:
048.             tp = gl_Cross.Move((ushort)lparam, (ushort)dparam);
049.             if (((uchar)sparam & MOUSE_LEFT) != 0)
050.             {
051.                 if (isPaint != "")
052.                 {
053.                     bMouseL = true;
054.                     if (!ObjectGetInteger(0, isPaint, OBJPROP_TIME)) ObjectMove(0, isPaint, 0, tp.Time, tp.Price);
055.                     ObjectMove(0, isPaint, 1, tp.Time, tp.Price);
056.                 }
057.             }else if (bMouseL) macro_CLEAN_EVENT;
058.             if ((((uchar)sparam & MOUSE_MIDDLE) != 0) && (isPaint == ""))
059.             {
060.                 ObjectCreate(0, isPaint = macro_NameObject, OBJ_FIBO, 0, 0, 0);
061.                 Modifier_OBJ_FIBO(isPaint);
062.                 gl_Cross.Show();
063.             }
064.             break;
065.         case CHARTEVENT_MOUSE_WHEEL:
066.             break;
067.     }
068.     ChartRedraw();
069. 
070. #undef macro_CLEAN_EVENT
071. };
072. //+------------------------------------------------------------------+
                   .
                   .
                   .

Код 06

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

Анимация 03

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

Во-первых, я добавил макрос в строку 32, который позволяет завершить событие создания рисунка в его текущем состоянии. Затем, в обработке событий мыши, я изменил порядок выполнения, чтобы события обрабатывались корректно: сначала нажимается средняя кнопка мыши; в этот момент строка 60 создает объект OBJ_FIBO, настраивает его по мере необходимости и отображает перекрестие.

В этот момент, если мы нажмем левую кнопку мыши, проверка строки 51 будет выполнена, и в строке 53 будет отмечена новая точка; одновременно строки 54 и 55 разместят объект OBJ_FIBO на графике. Метка, созданная в строке 53, позволяет в строке 57 удалить перекрестие с графика сразу после отрисовки объекта OBJ_FIBO, то есть при отпускании левой кнопки.

Чтобы исправить то, что было показано в анимации 03, нам необходимо изменить фрагмент кода 06, добавив две новые строки. Изменения отражены в следующем фрагменте кода:

                   .
                   .
                   .
029. //+------------------------------------------------------------------+
030. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
031. {
032. #define macro_CLEAN_EVENT   {                               \
033.             isPaint = "";                                   \
034.             gl_Cross.Hide();                                \
035.             bMouseL = false;                                \
036.             ChartSetInteger(0, CHART_MOUSE_SCROLL, true);   \
037.                             }
038. 
039.     st_TimePrice    tp;
040.     static string   isPaint = "";
041.     static bool     bMouseL = false;
042. 
043.     switch (id)
044.     {
045.         case CHARTEVENT_KEYDOWN:
046.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) macro_CLEAN_EVENT;
047.             break;
048.         case CHARTEVENT_MOUSE_MOVE:
049.             tp = gl_Cross.Move((ushort)lparam, (ushort)dparam);
050.             if (((uchar)sparam & MOUSE_LEFT) != 0)
051.             {
052.                 if (isPaint != "")
053.                 {
054.                     bMouseL = true;
055.                     if (!ObjectGetInteger(0, isPaint, OBJPROP_TIME)) ObjectMove(0, isPaint, 0, tp.Time, tp.Price);
056.                     ObjectMove(0, isPaint, 1, tp.Time, tp.Price);
057.                 }
058.             }else if (bMouseL) macro_CLEAN_EVENT;
059.             if ((((uchar)sparam & MOUSE_MIDDLE) != 0) && (isPaint == ""))
060.             {
061.                 ObjectCreate(0, isPaint = macro_NameObject, OBJ_FIBO, 0, 0, 0);
062.                 ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
063.                 Modifier_OBJ_FIBO(isPaint);
064.                 gl_Cross.Show();
065.             }
066.             break;
067.         case CHARTEVENT_MOUSE_WHEEL:
068.             break;
069.     }
070.     ChartRedraw();
071. 
072. #undef macro_CLEAN_EVENT
073. };
074. //+------------------------------------------------------------------+
                   .
                   .
                   .

Код 07

При использовании кода 07 мы получим результат, показанный в следующей анимации:

Анимация 04

Может показаться любопытным, что простой факт использования строк 36 и 62 во фрагменте кода 07 решает проблему анимации 03. Если вы так думаете, то это потому, что вы, возможно, не попробовали изложенное в предыдущих статьях, где я уже показывал эти самые функции, помимо прочих. Однако на тот момент мы ещё не включили и не выключили стандартную конфигурацию MetaTrader 5, чтобы обеспечить взаимодействие, подобное нынешнему.


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

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

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

Во фрагменте кода 07 строка 61 создает объект OBJ_FIBO до того, как перекрестие отображается в строке 64. Проблема в том, что если пользователь (или вы сами) нажимает клавишу ESCAPE, выполняется строка 46, и перекрестие удаляется из графика, но объект OBJ_FIBO, созданный в строке 61, остается в списке объектов, даже если он не виден, поскольку не был расположен.

Ваша задача — решить это таким образом, чтобы объект OBJ_FIBO добавлялся в список объектов только в том случае, если левая кнопка нажата после нажатия средней кнопки мыши. Помните проблему, которая возникла при создании объекта после генерации перекрестия, и как мы показали способ её решения?

Решение этой проблемы очень похоже: достаточно изменить порядок выполнения определенных операций во фрагменте кода 07; не потребуется создавать какие-либо подпрограммы, добавлять новые функции или процедуры. Если правильно переупорядочить операции, можно исключить перекрестие и предотвратить ненужное создание объекта OBJ_FIBO. Таким образом, пользователь может нажать клавишу ESCAPE, и в списке объектов не останется невидимый объект, который не отображается на графике.

Коды, использованные в данной статье, можно найти в приложении. Потренируйтесь в решении данной задачи, и мы увидимся в следующей статье.

Файл MQ5 Описание
Code 01 Демонстрация объекта
Code 02
Демонстрация объекта
Code 03  Демонстрация объекта
Code 04  Демонстрация объекта
Code 05  Демонстрация объекта

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

Прикрепленные файлы |
Anexo.zip (7.47 KB)
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Переосмысливаем классические стратегии (Часть 21): Разработка комбинированной стратегии на основе полос Боллинджера и RSI Переосмысливаем классические стратегии (Часть 21): Разработка комбинированной стратегии на основе полос Боллинджера и RSI
В этой статье рассматривается разработка комбинированной алгоритмической торговой стратегии для рынка EURUSD. Эта стратегия сочетает в себе полосы Боллинджера и индикатор относительной силы (RSI). Исходные стратегии, основанные на правилах, давали высококачественные сигналы, но страдали от низкой частоты сделок и ограниченной прибыльностью. Мы проанализировали несколько итераций стратегии, выявив недостатки в нашем понимании рынка, повышенный уровень шума и пониженную эффективность работы стратегии. Благодаря надлежащему использованию алгоритмов статистического обучения, переносу цели моделирования на технические индикаторы, правильному масштабированию и сочетанию прогнозов машинного обучения с классическими правилами торговли, конечная стратегия позволила значительно повысить прибыльность и частоту сделок при сохранении приемлемого качества сигнала.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Осциллятор Parafrac: Комбинация индикаторов Parabolic SAR и Fractals Осциллятор Parafrac: Комбинация индикаторов Parabolic SAR и Fractals
Мы рассмотрим, как объединить Parabolic SAR и индикатор Fractals для создания нового индикатора осцилляторного типа. Используя сильные стороны обоих инструментов, трейдеры могут разработать более точную и эффективную торговую стратегию.