English Español Português
preview
Разработка системы репликации (Часть 74): Новый Chart Trade (I)

Разработка системы репликации (Часть 74): Новый Chart Trade (I)

MetaTrader 5Примеры |
296 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье, "Разработка системы репликации (Часть 73): Неожиданный способ оповещений (II)", мы завершили второй этап разработки нашего приложения, которое будет отвечать за создание системы репликации/моделирования. Другими словами, пока что нам удалось заставить всю систему работать правильно. Вернее, нам удалось сделать так, чтобы мы могли представить нечто очень близкое к реальности движения рынка.

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

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

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

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

Важно отметить, что вся эта работа, которую мы проводим, чтобы показать, как всё делается, связана с программированием только на MQL5. Это делает работу еще более интересной с точки зрения реализации приложений. Задача по созданию инструмента, который работает как настоящий торговый сервер с использованием только MQL5, была очень интересной. Однако с точки зрения программирования гораздо проще создать приложение на языке C/C++, которое использует сокеты. Данная реализация на языке C/C++ должна генерировать все необходимые протоколы, чтобы MetaTrader 5 мог определить, что он находится в контакте с сервером.

Хотя такой подход многое упростил бы, поскольку позволил бы использовать MetaTrader 5 в его стандартной поставке, он не способствовал бы лучшему пониманию MQL5. Если бы такая реализация была выполнена на языке C/C++, то не было бы необходимости использовать MQL5. Подумайте об этом: много ценного опыта потерялось бы в плане знаний программирования на MQL5. Однако с тех пор, как я принял вызов создать что-то настолько сложное, как симулятор, используя только MQL5, мне удалось достичь поставленной цели. Сейчас мы вступаем в этап, где сделаем паузу в развитии репликации/моделирования. В течение некоторого времени вы обнаружите, что на демо-счете всё работает, но реализация всегда будет разработана с учетом использования репликации/моделирования.


Появление нового Chart Trade

Последний раз мы говорили о Chart Trade в статье "Разработка системы репликации (Часть 47)": Проект Chart Trade (VI). И хотя с тех пор мы не работали над ним, пришло время снова взяться за него. Однако с тех пор многое изменилось, и многое из того кода уже невозможно использовать. Этот код полностью устарел и должен быть исключен. Тем не менее, большинство концепций, пройденных в то время, по-прежнему актуальны и применимы. Мы создадим его снова, но с одной маленькой разницей: код будет полностью переписан, чтобы учесть новые концепции, разработанные к настоящему времени.

На данном этапе важно понять следующее: индикаторы НЕ МОГУТ ОТКРЫВАТЬ, МОДИФИЦИРОВАТЬ И ЗАКРЫВАТЬ позиции или ордера. Эта функция возложена на советника. Однако мы не будем рассматривать Chart Trade как код советника. Мы настроим его как индикатор, который будет взаимодействовать с советником для выполнения собственных задач.

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

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.          evHideMouse,               //Hide mouse price line
31.          evShowMouse,               //Show mouse price line
32.          evHideBarTime,             //Hide bar time
33.          evShowBarTime,             //Show bar time
34.          evHideDailyVar,            //Hide daily variation
35.          evShowDailyVar,            //Show daily variation
36.          evHidePriceVar,            //Hide instantaneous variation
37.          evShowPriceVar,            //Show instantaneous variation
38.          evCtrlReplayInit,          //Initialize replay control
39.          evChartTradeBuy,           //Market buy event
40.          evChartTradeSell,          //Market sales event 
41.          evChartTradeCloseAll       //Event to close positions
42.                   };
43. //+------------------------------------------------------------------+

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

Обратите внимание, что единственным изменением стало появление следующих строк 39, 40 y 41. Эти строки определяют события, которые будут инициированы, чтобы индикатор Chart Trade мог подать советнику сигнал к выполнению действия. Следовательно, именно советник выполняет действия по открытию и закрытию позиций. Важно отметить, что здесь речь идет не об ордерах, а о позициях. Chart Trade призван заменить стандартную систему MetaTrader 5, которая позволяет открывать и закрывать позиции увеличением размера сделки или частичным закрытием. Индикатор Chart Trade будет делать именно это, а также позволит нам применить те же концепции в репликации/моделировании. В этом случае сделки всегда будут совершаться по рыночной цене. Позже мы поработаем над другой системой, которая позволит использовать отложенные ордера, но пока мы будем двигаться шаг за шагом. Самая простая концепция, которую можно реализовать, - это исполнение по рыночной цены.

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

Далее мы рассмотрим измененный код индикатора и причины данных изменений, которые объясним позже. Для начала рассмотрим заголовочный файл C_AdjustTemplate.mqh. Полностью его можно увидеть ниже:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "../Auxiliar/C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_PATH_BTN "Images\\Market Replay\\Chart Trade"
007. #define def_BTN_BUY   def_PATH_BTN + "\\BUY.bmp"
008. #define def_BTN_SELL  def_PATH_BTN + "\\SELL.bmp"
009. #define def_BTN_DT    def_PATH_BTN + "\\DT.bmp"
010. #define def_BTN_SW    def_PATH_BTN + "\\SW.bmp"
011. #define def_BTN_MAX   def_PATH_BTN + "\\MAX.bmp"
012. #define def_BTN_MIN   def_PATH_BTN + "\\MIN.bmp"
013. #define def_IDE_RAD   def_PATH_BTN + "\\IDE_RAD.tpl"
014. #define def_IDE_RAD   "Files\\Chart Trade\\IDE_RAD.tpl"
015. //+------------------------------------------------------------------+
016. #resource "\\" + def_BTN_BUY
017. #resource "\\" + def_BTN_SELL
018. #resource "\\" + def_BTN_DT
019. #resource "\\" + def_BTN_SW
020. #resource "\\" + def_BTN_MAX
021. #resource "\\" + def_BTN_MIN
022. #resource "\\" + def_IDE_RAD as string IdeRad;
023. //+------------------------------------------------------------------+
024. class C_AdjustTemplate
025. {
026.    private   :
027.       string m_szName[],
028.              m_szFind[],
029.              m_szReplace[],
030.              m_szFileName;
031.       int    m_maxIndex,
032.              m_FileIn,
033.              m_FileOut;
034.       bool   m_bFirst;
035. //+------------------------------------------------------------------+
036.    public   :
037. //+------------------------------------------------------------------+
038.       C_AdjustTemplate(const string szFile, const bool bFirst = false)
039.          :m_maxIndex(0),
040.           m_szFileName(szFile),
041.           m_bFirst(bFirst),
042.           m_FileIn(INVALID_HANDLE),
043.           m_FileOut(INVALID_HANDLE)
044.          {
045.             ResetLastError();
046.             if (m_bFirst)
047.             {
048.                int handle = FileOpen(m_szFileName, FILE_TXT | FILE_WRITE);
049.                FileWriteString(handle, IdeRad);
050.                FileClose(handle);
051.             }
052.             if ((m_FileIn = FileOpen(m_szFileName, FILE_TXT | FILE_READ)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess);
053.             if ((m_FileOut = FileOpen(m_szFileName + "_T", FILE_TXT | FILE_WRITE)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess);
054.          }
055. //+------------------------------------------------------------------+
056.       ~C_AdjustTemplate()
057.          {
058.             FileClose(m_FileIn);
059.             FileClose(m_FileOut);
060.             FileMove(m_szFileName + "_T", 0, m_szFileName, FILE_REWRITE);
061.             ArrayResize(m_szName, 0);
062.             ArrayResize(m_szFind, 0);
063.             ArrayResize(m_szReplace, 0);
064.          }
065. //+------------------------------------------------------------------+
066.       void Add(const string szName, const string szFind, const string szReplace)
067.          {
068.             m_maxIndex++;
069.             ArrayResize(m_szName, m_maxIndex);
070.             ArrayResize(m_szFind, m_maxIndex);
071.             ArrayResize(m_szReplace, m_maxIndex);
072.             m_szName[m_maxIndex - 1] = szName;
073.             m_szFind[m_maxIndex - 1] = szFind;
074.             m_szReplace[m_maxIndex - 1] = szReplace;
075.          }
076. //+------------------------------------------------------------------+
077.       string Get(const string szName, const string szFind)
078.          {
079.             for (int c0 = 0; c0 < m_maxIndex; c0++) if ((m_szName[c0] == szName) && (m_szFind[c0] == szFind)) return m_szReplace[c0];
080.             
081.             return NULL;
082.          }
083. //+------------------------------------------------------------------+
084.       void Execute(void)
085.       bool Execute(void)
086.          {
087.             string sz0, tmp, res[];
088.             int count0 = 0, i0;
089.                         
090.             if ((m_FileIn != INVALID_HANDLE) && (m_FileOut != INVALID_HANDLE)) while ((!FileIsEnding(m_FileIn)) && (_LastError == ERR_SUCCESS))
091.             if ((m_FileIn == INVALID_HANDLE) || (m_FileOut == INVALID_HANDLE)) return false;
092.             while (!FileIsEnding(m_FileIn))
093.             {
094.                sz0 = FileReadString(m_FileIn);
095.                if (sz0 == "<object>") count0 = 1;
096.                if (sz0 == "</object>") count0 = 0;
097.                if (count0 > 0) if (StringSplit(sz0, '=', res) > 1)
098.                {
099.                   if ((m_bFirst) && ((res[0] == "bmpfile_on") || (res[0] == "bmpfile_off")))
100.                      sz0 = res[0] + "=\\Indicators\\Replay\\Chart Trade.ex5::" + def_PATH_BTN + res[1];
101.                      sz0 = res[0] + "=\\Indicators\\Chart Trade.ex5::" + def_PATH_BTN + res[1];
102.                   i0 = (count0 == 1 ? 0 : i0);
103.                   for (int c0 = 0; (c0 < m_maxIndex) && (count0 == 1); i0 = c0, c0++) count0 = (res[1] == (tmp = m_szName[c0]) ? 2 : count0);
104.                   for (int c0 = i0; (c0 < m_maxIndex) && (count0 == 2); c0++) if ((res[0] == m_szFind[c0]) && (tmp == m_szName[c0]))
105.                   {
106.                      if (StringLen(m_szReplace[c0])) sz0 =  m_szFind[c0] + "=" + m_szReplace[c0];
107.                      else m_szReplace[c0] = res[1];
108.                   }
109.                }
110.                if (FileWriteString(m_FileOut, sz0 + "\r\n") < 2) return false;
111.             };
112.             
113.             return true;
114.          }
115. //+------------------------------------------------------------------+
116. };
117. //+------------------------------------------------------------------+
118. #undef def_BTN_BUY
119. #undef def_BTN_SELL
120. #undef def_BTN_DT
121. #undef def_BTN_SW
122. #undef def_BTN_MAX
123. #undef def_BTN_MIN
124. #undef def_IDE_RAD
125. #undef def_PATH_BTN
126. //+------------------------------------------------------------------+

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

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

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

Теперь давайте рассмотрим случай со строкой 90: почему она была удалена? Причина в том, что переменная _LastError может содержать значение, отличающееся от ERR_SUCCESS, которое не было вызвано нашим приложением. Однако именно для того, чтобы избежать потенциального конфликта интересов, ситуация здесь изменилась.

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

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

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

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

Прежде чем завершить объяснение этого заголовочного файла, надо обратить внимание на содержимое строки 101. Прошу заметить, что строка 100 была удалена, а на ее месте появилась строка 101. То, что я хочу выделить - это текстовая строка. Она точно указывает на предполагаемое местоположение индикатора Chart Trade, то есть местоположение изменилось.

Теперь мы должны поместить индикатор именно туда, как это указано в строке с установленным именем. Это связано с тем, что при использовании данной строки происходит фактический доступ к растровым файлам, объявленным в индикаторе в качестве ресурсов. Если изменить здесь что-либо в коде, в названии индикатора или в его расположении, MetaTrader 5 не сможет получить доступ к растровым файлам, и индикатор Chart Trade не будет правильно отображаться на графике.

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Chart Trade Base Indicator."
04. #property description "See the articles for more details."
05. #property version   "1.74"
06. #property icon "/Images/Market Replay/Icons/Indicators.ico"
07. #property link "https://www.mql5.com/pt/articles/12413"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. #property indicator_buffers 1
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
13. //+------------------------------------------------------------------+
14. #define def_ShortName "Indicator Chart Trade"
15. //+------------------------------------------------------------------+
16. C_ChartFloatingRAD *chart = NULL;
17. //+------------------------------------------------------------------+
18. input long     user00 = 0;          //ID
19. input ushort   user01 = 1;          //Leverage
20. input double   user02 = 100.1;      //Finance Take
21. input double   user03 = 75.4;       //Finance Stop
22. //+------------------------------------------------------------------+
23. double m_Buff[];
24. //+------------------------------------------------------------------+
25. int OnInit()
26. {
27.    bool bErr;
28.       
29.    chart = new C_ChartFloatingRAD(user00, "Indicator Chart Trade", new C_Mouse(user00, "Indicator Mouse Study"), user01, user02, user03);
30.    chart = new C_ChartFloatingRAD(def_ShortName, new C_Mouse(0, "Indicator Mouse Study"), user01, user02, user03);
31.    
32.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
33. 
34.    if (bErr = (_LastError != ERR_SUCCESS)) Print(__FILE__, " - [Error]: ", _LastError);
35. 
36.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
37.    ArrayInitialize(m_Buff, EMPTY_VALUE);
38.    
39.    return (bErr ? INIT_FAILED : INIT_SUCCEEDED);
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.    (*chart).MountBuffer(m_Buff, rates_total);
46.    
47.    return rates_total;
48. }
49. //+------------------------------------------------------------------+
50. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
51. {
52.    if (_LastError < ERR_USER_ERROR_FIRST) 
53.       (*chart).DispatchMessage(id, lparam, dparam, sparam);
54.    (*chart).MountBuffer(m_Buff);
55.    
56.    ChartRedraw();
57. }
58. //+------------------------------------------------------------------+
59. void OnDeinit(const int reason)
60. {
61.    switch (reason)
62.    {
63.       case REASON_INITFAILED:
64.          ChartIndicatorDelete(ChartID(), 0, def_ShortName);
65.          break;
66.       case REASON_CHARTCHANGE:
67.          (*chart).SaveState();
68.          break;
69.    }
70. 
71.    delete chart;
72. }
73. //+------------------------------------------------------------------+

Исходный код индикатора Chart Trade

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

Если вы не видели это, то можете ознакомиться с исходным кодом в статье "Разработка системы репликации (Часть 47): Проект Chart Trade (VI). Однако не пытайтесь использовать код, приведенный в данной статье. Он работает совершенно по-другому. Хотя это может показаться сложным, на самом деле всё очень просто. Причина такой простоты заключается в том, что все сложности были перенесены в заголовочный файл. Теперь давайте посмотрим, как работает код, поскольку он совершенно необходим для понимания заголовочного файла, который мы рассмотрим позже.

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

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

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

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

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

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

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

Посмотрите, к примеру, как относительно простая вещь может стать сложной во многих случаях. Что же произойдет, если мы используем Chart Trade в советнике? Это кажется чем-то невыполнимым. На самом деле, можно частично согласиться, так как интегрировать Chart Trade в советник действительно можно, но это нецелесообразно.

И почему это невыполнимо? Причина проста: диверсификация. Давайте подумаем: если мы разместим Chart Trade внутри советника, он будет доступен только в этом советнике. Если мы по какой-то причине захотим создать еще один советник, нам придется добавить весь код Chart Trade в этот новый советник. Хорошо, но можно легко додуматься используя включения из строки 12, которое, конечно, будет отлично работать. Но а что, если мы решим что-то улучшить в коде Chart Trade? Чтобы обновить код, придется перекомпилировать все советники. Это потребует много работы, в то время как первое решение стало бы более практичным.

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

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

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

Строку 18 удалили, так как нет необходимости использовать этот ресурс. Теперь пользователь сам будет размещать индикатор Chart Trade на графике, поэтому нет необходимости указывать ID графика. С другой стороны, строка 23 была исключена, но не из-за исключения строки 10, поскольку это разные вещи. Как объяснялось выше, строка 10 используется, чтобы определить существует ли он. Если ответ положительный, то указывается, ко скольким буферам получен доступ извне к индикатору через CopyBuffer. Но это не мешает индикатору иметь внутренние буферы. Однако, поскольку в них нет необходимости, строку 23 исключили. То же самое верно и для строк 36 и 37, поскольку буфер не используется, эти строки также не нужны.

В этом коде OnInit можно увидеть множество удаленных строк. Во многом это связано с изменениями, которые опишем подробно в следующей статье, где будет рассмотрен код заголовочного файла C_ChartFloatingRAD.mqh. Среди всех удаленных строк только три выполняют активную функцию. Давайте посмотрим на них, начиная со строки 30. Данная строка запускает класс C_ChartFloatingRAD. Это делается через конструктор с помощью вызова оператора new. Однако, как вы, возможно, уже знаете, конструкторы не возвращают никаких значений. Чтобы проверить, не дал ли сбой код конструктора, можно использовать другие методы.

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

К счастью, MQL5 позволяет нам сделать это. Правда, этот способ не самый подходящий, но работает. В строке 32 мы проверяем значение переменной _LastError. Если она содержит значение, определенное нами, это означает, что инициализация не удалась. Я не уверен, что оставлю данную структуру в прежнем виде, но благодаря строке 52 я могу это сделать. Впрочем, мы это еще обсудим. Если строка 32 обнаруживает, что инициализация не удалась, в таком случае мы заставим функцию OnInit вернуть константу ошибки в MetaTrader 5. Получив данную константу, MetaTrader 5 в определенный момент вызовет OnDeInit, который автоматически удалит индикатор с графика. Если всё работает правильно, OnInit вернет константу об успехе, что и происходит в строке 40.

Теперь посмотрите на строки 45 и 54. Оба варианта использовались для настройки буфера. Однако, поскольку мы больше не используем буферы, эти строки уже устарели и были удалены, что хорошо видно по тому, как они выделены. Но обратите внимание на тест в строке 52. В настоящее время данный тест не имеет смысла, но без него вызов строки 53 в какой-то момент завершится неудачей. Причину, по которой данный вызов в строке 53 окажется неудачным без теста в строке 52, можно будет понять только в следующей статье, где будет подробно рассмотрен класс C_ChartFloatingRAD.

В заключение давайте рассмотрим последний элемент, присутствующий в этом коде: процедуру OnDeInit. Данная процедура вызывается автоматически, когда индикатор должен быть удален с графика. Однако здесь мы видим нечто необычное для OnDeInit. Я имею в виду строку 64. Почему эта строка включена в код? На первый взгляд, в этом нет никакого смысла. И на самом деле нет, но это потому, что вы смотрите на код, не зная о классе C_ChartFloatingRAD. Если ограничиться только данным фрагментом кода, то единственной причиной, по которой REASON_INITFAILED может быть константой, используемой в вызове OnDeInit, является строка 32. Однако в коде класса C_ChartFloatingRAD есть и другие причины. Поскольку я хочу централизовать выполнение определенных действий, появляется строка 64. Эта строка удаляет с графика именно индикатор Chart Trade. Но настоящую причину опишем в следующей статье.


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

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

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

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

Прикрепленные файлы |
Anexo.zip (9.92 KB)
Возможности Мастера MQL5, которые вам нужно знать (Часть 45): Обучение с подкреплением с помощью метода Монте-Карло Возможности Мастера MQL5, которые вам нужно знать (Часть 45): Обучение с подкреплением с помощью метода Монте-Карло
Монте-Карло — четвертый алгоритм обучения с подкреплением, который мы рассматриваем в контексте его реализации в советниках, собранных с помощью Мастера. Хотя алгоритм основан на случайной выборке, он предоставляет обширные возможности моделирования.
Отправка запросов в Connexus (Часть 6): Создание HTTP-запроса и ответа Отправка запросов в Connexus (Часть 6): Создание HTTP-запроса и ответа
В этой шестой статье из серии о библиотеке Connexus мы сосредоточимся на полном HTTP-запросе, рассмотрев каждый компонент, из которого состоит запрос. Мы создадим класс, представляющий запрос в целом, который поможет нам объединить ранее созданные классы.
Нейросети в трейдинге: Фреймворк кросс-доменного прогнозирования временных рядов (Окончание) Нейросети в трейдинге: Фреймворк кросс-доменного прогнозирования временных рядов (Окончание)
Статья посвящена практическому построению модели TimeFound для прогнозирования временных рядов. Рассматриваются ключевые этапы реализации основных подходов фреймворка средствами MQL5.
Разработка инструментария для анализа движения цен (Часть 1): Проектор графиков Разработка инструментария для анализа движения цен (Часть 1): Проектор графиков
Настоящий проект направлен на использование алгоритма MQL5 для разработки комплексного набора инструментов анализа для MetaTrader 5. Эти инструменты — от скриптов и индикаторов до моделей искусственного интеллекта и советников — позволят автоматизировать процесс анализа рынка. Иногда такая разработка позволяет создавать инструменты, способные выполнять углубленный анализ без участия человека и прогнозировать результаты на соответствующих платформах. Ни одна возможность не будет упущена. Присоединяйтесь ко мне в рамках исследования процесса создания надежного набора пользовательских инструментов для анализа рынка. Начнем с разработки простой программы на MQL5, которую я назвал Chart Projector (Проектор графиков).