English 中文 Español Deutsch 日本語 Português
preview
Разработка системы репликации (Часть 49): Все усложняется (I)

Разработка системы репликации (Часть 49): Все усложняется (I)

MetaTrader 5Примеры |
581 1
Daniel Jose
Daniel Jose

Введение

В этой статье мы будем использовать то, что увидели в статье Разработка системы репликации (Часть 48): Концепции для понимания и осмысления. Так что если вы ее не читали, прочтите, потому что содержание этой статьи очень важно для понимания того, что мы будем здесь делать.

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

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

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

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

Давайте посмотрим, как это будет реализовано, поскольку изменения будут очень интересны для внедрения и дадут нам отличные знания о том, как более профессионально работать на MQL5. И подчеркну, что это не предполагает задержки в развитии нашей системы репликации/моделирования. Фактически, системе нужны некоторые вещи, которые мы изучим в этой статье, чтобы в будущем программировать что-то другое.

Итак, давайте продолжим наше эпическое продвижение в сторону более продвинутой реализации сервиса репликации/моделирования.


Начало изменений

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

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

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_SymbolReplay               "RePlay"
05. #define def_GlobalVariableReplay       def_SymbolReplay + "_Infos"
06. #define def_GlobalVariableIdGraphics   def_SymbolReplay + "_ID"
07. #define def_GlobalVariableServerTime   def_SymbolReplay + "_Time"
08. #define def_MaxPosSlider               400
09. //+------------------------------------------------------------------+
10. union u_Interprocess
11. {
12.     union u_0
13.     {
14.             double  df_Value;       // Value of the terminal global variable...
15.             ulong   IdGraphic;      // Contains the Graph ID of the asset...
16.     }u_Value;
17.     struct st_0
18.     {
19.             bool    isPlay;         // Indicates whether we are in Play or Pause mode...
20.             bool    isWait;         // Tells the user to wait...
21.             bool    isHedging;      // If true we are in a Hedging account, if false the account is Netting...
22.             bool    isSync;         // If true indicates that the service is synchronized...
23.             ushort  iPosShift;      // Value between 0 and 400...
24.     }s_Infos;
25.     datetime        ServerTime;
26. };
27. //+------------------------------------------------------------------+
28. union uCast_Double
29. {
30.     double   dValue;
31.     long     _long;                  // 1 Information
32.     datetime _datetime;              // 1 Information
33.     int      _int[sizeof(double)];   // 2 Informations
34.     char     _char[sizeof(double)];  // 8 Informations
35. };
36. //+------------------------------------------------------------------+

Исходный код Interprocess.mqh

Если посмотреть на строку 06, там приводится определение, которое указывает как сервису, так и индикатору, какое имя глобальной переменной терминала будет использоваться для передачи идентификатора графика. Удаление этой строки при попытке скомпилировать индикатор и сервис, приведет к серии ошибок. И это именно то, что нам нужно. Фактически, каждая ошибка, генерируемая при компиляции, указывает на точку, где нам следует вмешаться, чтобы устранить эту связь, существующую между сервисом и индикатором, через глобальную переменную терминала.

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

Итак, начнем с рассмотрения следующего факта:

01. //+------------------------------------------------------------------+
02. #property service
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property copyright "Daniel Jose"
05. #property version   "1.49"
06. #property description "Replay-Simulator service for MT5 platform."
07. #property description "This is dependent on the Market Replay indicator."
08. #property description "For more details on this version see the article."
09. #property link "https://www.mql5.com/ru/articles/11820"
10. //+------------------------------------------------------------------+
11. #define def_Dependence_01   "Indicators\\Replay\\Market Replay.ex5"
12. #resource "\\" + def_Dependence_01
13. //+------------------------------------------------------------------+
14. #include <Market Replay\Service Graphics\C_Replay.mqh>
15. //+------------------------------------------------------------------+
16. input string           user00 = "Forex - EURUSD.txt";  //Replay configuration file.
17. input ENUM_TIMEFRAMES  user01 = PERIOD_M5;             //Initial graphic time.
18. //+------------------------------------------------------------------+
19. void OnStart()
20. {
21.     C_Replay  *pReplay;
22. 
23.     pReplay = new C_Replay(user00);
24.     if ((*pReplay).ViewReplay(user01))
25.     {
26.             Print("Permission granted. Replay service can now be used...");
27.             while ((*pReplay).LoopEventOnTime(false));
28.     }
29.     delete pReplay;
30. }
31. //+------------------------------------------------------------------+

Исходный код: Сервис replay.mq5

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

И да, на момент написания этой статьи у нас еще нет системы компиляции MAKE в MetaEditor. Но кто знает, возможно в будущем те, кто поддерживает платформу и язык MQL, реализуют для нас эту возможность. Но до тех пор нам нужно соблюдать определенные меры предосторожности.

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

Итак, по факту, прежде чем компилировать что-то, вы создаете еще один файл. Это файл MakeFile, который содержит ряд шагов, определений, конфигураций и настроек, необходимых для того, чтобы компилятор и LinkEditor могли сгенерировать все исполняемые файлы за один раз. Большим преимуществом этой системы является то, что если вы измените один файл, будь то заголовок или библиотека, MAKE обнаружит это и скомпилирует только те файлы, которые действительно необходимы. Таким образом, в конечном итоге у вас будут все исполняемые файлы, обновляемые по мере необходимости.

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

Однако, на момент написания статьи такого инструмента в MetaEditor еще нет. Можно создать нечто подобное, используя пакетные файлы из командной строки, но это будет всего лишь временное решение, а не идеальный вариант. Поэтому я не буду вдаваться в подробности того, как это делается. Но поскольку строка 12 приведенного выше кода указывает компилятору MQL5, что исполняемый файл контрольного индикатора должен быть добавлен к сервису, вы можете просто попросить скомпилировать код сервиса, и код индикатора будет скомпилирован вместе с ним. Я уже говорил об этом ранее, но здесь еще раз подчеркну: чтобы индикатор был правильно скомпилировн, уже с обновлениями, каждый раз при компиляции кода сервиса вам нужно будет удалять исполняемый файл индикатора. Если этого не сделать, код индикатора не будет компилироваться заново при компиляции кода сервиса. Так что обратите на это внимание.

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

Рисунок 01

Изображение 01 — Результат попытки скомпилировать новый код

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

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

А вот ошибка, указанная в строке 156, является частью кода сервиса, и мы должны ее исправить, чтобы продолжить компиляцию.

Ниже представлен фрагмент кода, где находится ошибка. 

141. ~C_Replay()
142.    {
143.            ArrayFree(m_Ticks.Info);
144.            ArrayFree(m_Ticks.Rate);
145.            m_IdReplay = ChartFirst();
146.            do
147.            {
148.                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
149.                            ChartClose(m_IdReplay);
150.            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
151.            for (int c0 = 0; (c0 < 2) && (!SymbolSelect(def_SymbolReplay, false)); c0++);
152.            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
153.            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
154.            CustomSymbolDelete(def_SymbolReplay);
155.            GlobalVariableDel(def_GlobalVariableReplay);
156.            GlobalVariableDel(def_GlobalVariableIdGraphics);
157.            GlobalVariableDel(def_GlobalVariableServerTime);
158.            Print("Finished replay service...");
159.    }

Фрагмент кода C_Replay.mqh

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

Рисунок 02

Изображение 02 — Новая попытка компиляции

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

Обратите внимание на изображение 02. Отмечу, что прямо над сообщением об ошибке нам предоставляется некоторая другая информация. НИКОГДА не игнорируйте то, что сообщает компилятор. Следует обращать внимание абсолютно на ВСЕ. И в сообщении, непосредственно предшествующем указанию на ошибку, можно увидеть следующую информацию от компилятора:

compiling '\Indicators\Replay\Market Replay.mq5' failed

Это сообщение является ключевым, поскольку сообщает нам, что компилятор по какой-то причине не может создать исполняемый файл индикатора. Поскольку код сервиса частично компилируется, нам нужно сосредоточить внимание на коде индикатора. Мы открываем указанный код в MetaEditor и просим компилятор попытаться сгенерировать исполняемый файл. И прежде чем кто-нибудь подумает: «Но зачем это делать, ведь известно, что код содержит ошибки?», отвечу: да, мы знаем, что они есть, но хотим, чтобы компилятор указал нам, где именно. Искать ошибки вручную непродуктивно. Позвольте компилятору вам на них указать.

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

Рисунок 03

Изображение 03 — Попытка скомпилировать контрольный индикатор

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

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.49"
07. #property link "https://www.mql5.com/ru/articles/11820"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. #define def_BitShift ((sizeof(ulong) * 8) - 1)
14. //+------------------------------------------------------------------+
15. C_Terminal *terminal = NULL;
16. C_Controls *control = NULL;
17. //+------------------------------------------------------------------+
18. #define def_InfoTerminal (*terminal).GetInfoTerminal()
19. #define def_ShortName       "Market_" + def_SymbolReplay
20. //+------------------------------------------------------------------+
21. int OnInit()
22. {
23. #define macro_INIT_FAILED { ChartIndicatorDelete(def_InfoTerminal.ID, 0, def_ShortName); return INIT_FAILED; }
24.     u_Interprocess Info;
25.     ulong ul = 1;
26. 
27.     ResetLastError();
28.     if (CheckPointer(control = new C_Controls(terminal = new C_Terminal())) == POINTER_INVALID) return INIT_FAILED;
29.     if (_LastError != ERR_SUCCESS) return INIT_FAILED;
30.     ul <<= def_BitShift;
31.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
32.     if ((def_InfoTerminal.szSymbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics))) macro_INIT_FAILED;
33.     Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
34.     if (Info.u_Value.IdGraphic != def_InfoTerminal.ID) macro_INIT_FAILED;
35.     if ((Info.u_Value.IdGraphic >> def_BitShift) == 1) macro_INIT_FAILED;
36.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device");
37.     Info.u_Value.IdGraphic |= ul;
38.     GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value);
39.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
40.     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.u_Value.df_Value, "");
41.     (*control).Init(Info.s_Infos.isPlay);
42.         
43.     return INIT_SUCCEEDED;
44.     
45. #undef macro_INIT_FAILED
46. }
47. //+------------------------------------------------------------------+
48. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
49. {
50.     static bool bWait = false;
51.     u_Interprocess Info;
52.     
53.     Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
54.     if (!bWait)
55.     {
56.             if (Info.s_Infos.isWait)
57.             {
58.                     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOn, 1, 0, "");
59.                     bWait = true;
60.             }
61.     }else if (!Info.s_Infos.isWait)
62.     {
63.             EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.u_Value.df_Value, "");
64.             bWait = false;
65.     }
66.     
67.     return rates_total;
68. }
69. //+------------------------------------------------------------------+
70. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
71. {
72.     (*control).DispatchMessage(id, lparam, dparam, sparam);
73. }
74. //+------------------------------------------------------------------+
75. void OnDeinit(const int reason)
76. {
77.     u_Interprocess Info;
78.     ulong ul = 1;
79. 
80.     switch (reason)
81.     {
82.             case REASON_CHARTCHANGE:
83.                     ul <<= def_BitShift;
84.                     Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
85.                     Info.u_Value.IdGraphic ^= ul;
86.                     GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value);
87.                     break;
88.             case REASON_REMOVE:
89.             case REASON_CHARTCLOSE:
90.                     if (def_InfoTerminal.szSymbol != def_SymbolReplay) break;
91.                     GlobalVariableDel(def_GlobalVariableReplay);
92.                     ChartClose(def_InfoTerminal.ID);
93.                     break;
94.     }
95.     delete control;
96.     delete terminal;
97. }
98. //+------------------------------------------------------------------+

Исходный код индикатора: Market replay.mq5

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

Рисунок 04

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

Обратите внимание, что на этом изображении 04 есть предупреждение. Оно на самом деле коду не мешает, но может раздражать. Поэтому перейдите к строке 77 и, удалив ее, попробуйте скомпилировать код снова. Вы получите сообщение, показанное на изображении 05, и это именно то, что нам нужно. Внимание: строка 77 может быть удалена только потому, что компилятор сообщил нам, что присутствующая там переменная не используется.

Рисунок 05

Изображение 05 — Компиляция успешно завершена

Очень-очень хорошо, и даже отлично. Наш код был частично очищен. Однако, возвращаясь к коду заголовочного файла Interprocess.mqh, показанному в начале этой темы, можно заметить, что в коде все еще присутствует нечто, что нам больше не понадобится. Это связано с тем, что мы больше не используем глобальную переменную терминала для передачи индикатору идентификатора графика. Поэтому строка 15 файла Interprocess.mqh должна быть удалена. Но возникает вопрос: «Почему я не удалил эту строку раньше?». Причина в том, что к файлу сервиса следует относиться с особым вниманием. Но есть и другая причина, и чтобы понять ее, перейдем к следующей теме.


Радикализация решений

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

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

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

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

Главное достоинство великого программиста заключается именно в этом, — НЕ ТОРОПИТЬСЯ. Решать проблему за проблемой, постепенно исправляя и изменяя вещи так, чтобы сохранять все уже имеющиеся возможности программы, и, если нужно или интересно, расширять их и приумножать. Поэтому, девиз таков: 

Разделяй и властвуй.

Посмотрим, как будет очищаться код. Новый код в файле Interprocess.mqh целиком можно увидеть ниже:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_SymbolReplay             "RePlay"
05. #define def_GlobalVariableReplay     def_SymbolReplay + "_Infos"
06. #define def_GlobalVariableServerTime def_SymbolReplay + "_Time"
07. #define def_MaxPosSlider             400
08. //+------------------------------------------------------------------+
09. union u_Interprocess
10. {
11.     double  df_Value; // Value of the terminal global variable...
12.     struct st_0
13.     {
14.             bool    isPlay;     // Indicates whether we are in Play or Pause mode...
15.             bool    isWait;     // Tells the user to wait...
16.             bool    isHedging;  // If true we are in a Hedging account, if false the account is Netting...
17.             bool    isSync;     // If true indicates that the service is synchronized...
18.             ushort  iPosShift;  // Value between 0 and 400...
19.     }s_Infos;
20.     datetime ServerTime;
21. };
22. //+------------------------------------------------------------------+
23. union uCast_Double
24. {
25.     double  dValue;
26.     long     _long;                  // 1 Information
27.     datetime _datetime;              // 1 Information
28.     int      _int[sizeof(double)];   // 2 Informations
29.     char     _char[sizeof(double)];  // 8 Informations
30. };
31. //+------------------------------------------------------------------+

Исходный код: Interprocess.mqh

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

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

Рисунок 06

Изображение 06 – Множество ошибок. Но действительно ли их так много?

Обратите внимание, что количество ошибок было довольно большим, также как и количество предупреждений. Как я уже упоминал выше, мы начнем с первой ошибки в этом списке. Поэтому, при внесении изменений, они начнутся с 28 строки заголовочного файла C_Replay.mqh. Чтобы это объяснение не было слишком утомительным, давайте посмотрим на приведенный ниже код, поскольку большая часть того, что нам нужно будет сделать, — это удалить из него u_Value. Код уже без этой ссылки можно увидеть целиком ниже:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_ConfigService.mqh"
005. //+------------------------------------------------------------------+
006. class C_Replay : private C_ConfigService
007. {
008.    private :
009.            long   m_IdReplay;
010.            struct st01
011.            {
012.                   MqlRates Rate[1];
013.                   datetime memDT;
014.            }m_MountBar;
015.            struct st02
016.            {
017.                   bool    bInit;
018.                   double  PointsPerTick;
019.                   MqlTick tick[1];
020.            }m_Infos;
021. //+------------------------------------------------------------------+
022.            void AdjustPositionToReplay(const bool bViewBuider)
023.                    {
024.                            u_Interprocess Info;
025.                            MqlRates       Rate[def_BarsDiary];
026.                            int            iPos, nCount;
027.                            
028.                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
029.                            if (Info.s_Infos.iPosShift == (int)((m_ReplayCount * def_MaxPosSlider * 1.0) / m_Ticks.nTicks)) return;
030.                            iPos = (int)(m_Ticks.nTicks * ((Info.s_Infos.iPosShift * 1.0) / (def_MaxPosSlider + 1)));
031.                            Rate[0].time = macroRemoveSec(m_Ticks.Info[iPos].time);
032.                            CreateBarInReplay(true);
033.                            if (bViewBuider)
034.                            {
035.                                    Info.s_Infos.isWait = true;
036.                                    GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
037.                            }else
038.                            {
039.                                    for(; Rate[0].time > (m_Ticks.Info[m_ReplayCount].time); m_ReplayCount++);
040.                                    for (nCount = 0; m_Ticks.Rate[nCount].time < macroRemoveSec(m_Ticks.Info[iPos].time); nCount++);
041.                                    nCount = CustomRatesUpdate(def_SymbolReplay, m_Ticks.Rate, nCount);
042.                            }
043.                            for (iPos = (iPos > 0 ? iPos - 1 : 0); (m_ReplayCount < iPos) && (!_StopFlag);) CreateBarInReplay(false);
044.                            CustomTicksAdd(def_SymbolReplay, m_Ticks.Info, m_ReplayCount);
045.                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
046.                            Info.s_Infos.isWait = false;
047.                            GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
048.                    }
049. //+------------------------------------------------------------------+
050. inline void CreateBarInReplay(const bool bViewTicks)
051.                    {
052. #define def_Rate m_MountBar.Rate[0]
053. 
054.                            bool    bNew;
055.                            double  dSpread;
056.                            int     iRand = rand();
057.                            
058.                            if (BuildBar1Min(m_ReplayCount, def_Rate, bNew))
059.                            {
060.                                    m_Infos.tick[0] = m_Ticks.Info[m_ReplayCount];
061.                                    if ((!m_Ticks.bTickReal) && (m_Ticks.ModePlot == PRICE_EXCHANGE))
062.                                    {                                               
063.                                            dSpread = m_Infos.PointsPerTick + ((iRand > 29080) && (iRand < 32767) ? ((iRand & 1) == 1 ? m_Infos.PointsPerTick : 0 ) : 0 );
064.                                            if (m_Infos.tick[0].last > m_Infos.tick[0].ask)
065.                                            {
066.                                                    m_Infos.tick[0].ask = m_Infos.tick[0].last;
067.                                                    m_Infos.tick[0].bid = m_Infos.tick[0].last - dSpread;
068.                                            }else   if (m_Infos.tick[0].last < m_Infos.tick[0].bid)
069.                                            {
070.                                                    m_Infos.tick[0].ask = m_Infos.tick[0].last + dSpread;
071.                                                    m_Infos.tick[0].bid = m_Infos.tick[0].last;
072.                                            }
073.                                    }
074.                                    if (bViewTicks) CustomTicksAdd(def_SymbolReplay, m_Infos.tick);
075.                                    CustomRatesUpdate(def_SymbolReplay, m_MountBar.Rate);
076.                            }
077.                            m_ReplayCount++;
078. #undef def_Rate
079.                    }
080. //+------------------------------------------------------------------+
081.            void ViewInfos(void)
082.                    {
083.                            MqlRates Rate[1];
084.                            
085.                            ChartSetInteger(m_IdReplay, CHART_SHOW_ASK_LINE, m_Ticks.ModePlot == PRICE_FOREX);
086.                            ChartSetInteger(m_IdReplay, CHART_SHOW_BID_LINE, m_Ticks.ModePlot == PRICE_FOREX);
087.                            ChartSetInteger(m_IdReplay, CHART_SHOW_LAST_LINE, m_Ticks.ModePlot == PRICE_EXCHANGE);
088.                            m_Infos.PointsPerTick = SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE);
089.                            m_MountBar.Rate[0].time = 0;
090.                            m_Infos.bInit = true;
091.                            CopyRates(def_SymbolReplay, PERIOD_M1, 0, 1, Rate);
092.                            if ((m_ReplayCount == 0) && (m_Ticks.ModePlot == PRICE_EXCHANGE))
093.                                    for (; m_Ticks.Info[m_ReplayCount].volume_real == 0; m_ReplayCount++);
094.                            if (Rate[0].close > 0)
095.                            {
096.                                    if (m_Ticks.ModePlot == PRICE_EXCHANGE) m_Infos.tick[0].last = Rate[0].close; else
097.                                    {
098.                                            m_Infos.tick[0].bid = Rate[0].close;
099.                                            m_Infos.tick[0].ask = Rate[0].close + (Rate[0].spread * m_Infos.PointsPerTick);
100.                                    }                                       
101.                                    m_Infos.tick[0].time = Rate[0].time;
102.                                    m_Infos.tick[0].time_msc = Rate[0].time * 1000;
103.                            }else
104.                                    m_Infos.tick[0] = m_Ticks.Info[m_ReplayCount];
105.                            CustomTicksAdd(def_SymbolReplay, m_Infos.tick);
106.                            ChartRedraw(m_IdReplay);
107.                    }
108. //+------------------------------------------------------------------+
109.            void CreateGlobalVariable(const string szName, const double value)
110.                    {
111.                            GlobalVariableDel(szName);
112.                            GlobalVariableTemp(szName);     
113.                            GlobalVariableSet(szName, value);
114.                    }
115. //+------------------------------------------------------------------+
116.    public  :
117. //+------------------------------------------------------------------+
118.            C_Replay(const string szFileConfig)
119.                    {
120.                            m_ReplayCount = 0;
121.                            m_dtPrevLoading = 0;
122.                            m_Ticks.nTicks = 0;
123.                            m_Infos.bInit = false;
124.                            Print("************** Market Replay Service **************");
125.                            srand(GetTickCount());
126.                            GlobalVariableDel(def_GlobalVariableReplay);
127.                            SymbolSelect(def_SymbolReplay, false);
128.                            CustomSymbolDelete(def_SymbolReplay);
129.                            CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay), _Symbol);
130.                            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
131.                            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
132.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE, 0);
133.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE, 0);
134.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP, 0);
135.                            CustomSymbolSetString(def_SymbolReplay, SYMBOL_DESCRIPTION, "Symbol for replay / simulation");
136.                            CustomSymbolSetInteger(def_SymbolReplay, SYMBOL_DIGITS, 8);
137.                            m_IdReplay = (SetSymbolReplay(szFileConfig) ? 0 : -1);
138.                            SymbolSelect(def_SymbolReplay, true);
139.                    }
140. //+------------------------------------------------------------------+
141.            ~C_Replay()
142.                    {
143.                            ArrayFree(m_Ticks.Info);
144.                            ArrayFree(m_Ticks.Rate);
145.                            m_IdReplay = ChartFirst();
146.                            do
147.                            {
148.                                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
149.                                            ChartClose(m_IdReplay);
150.                            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
151.                            for (int c0 = 0; (c0 < 2) && (!SymbolSelect(def_SymbolReplay, false)); c0++);
152.                            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
153.                            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
154.                            CustomSymbolDelete(def_SymbolReplay);
155.                            GlobalVariableDel(def_GlobalVariableReplay);
156.                            GlobalVariableDel(def_GlobalVariableServerTime);
157.                            Print("Finished replay service...");
158.                    }
159. //+------------------------------------------------------------------+
160.            bool ViewReplay(ENUM_TIMEFRAMES arg1)
161.                    {
162. #define macroError(A) { Print(A); return false; }
163.                            u_Interprocess info;
164.                            
165.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE) == 0)
166.                                    macroError("Asset configuration is not complete, it remains to declare the size of the ticket.");
167.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE) == 0)
168.                                    macroError("Asset configuration is not complete, need to declare the ticket value.");
169.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP) == 0)
170.                                    macroError("Asset configuration not complete, need to declare the minimum volume.");
171.                            if (m_IdReplay == -1) return false;
172.                            if ((m_IdReplay = ChartFirst()) > 0) do
173.                            {
174.                                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
175.                                    {
176.                                            ChartClose(m_IdReplay);
177.                                            ChartRedraw();
178.                                    }
179.                            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
180.                            Print("Waiting for [Market Replay] indicator permission to start replay ...");
181.                            info.ServerTime = ULONG_MAX;
182.                            CreateGlobalVariable(def_GlobalVariableServerTime, info.df_Value);
183.                            m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
184.                            ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");
185.                            while ((!GlobalVariableGet(def_GlobalVariableReplay, info.df_Value)) && (!_StopFlag) && (ChartSymbol(m_IdReplay) != "")) Sleep(750);
186.                            info.s_Infos.isHedging = TypeAccountIsHedging();
187.                            info.s_Infos.isSync = true;
188.                            GlobalVariableSet(def_GlobalVariableReplay, info.df_Value);
189. 
190.                            return ((!_StopFlag) && (ChartSymbol(m_IdReplay) != ""));
191. #undef macroError
192.                    }
193. //+------------------------------------------------------------------+
194.            bool LoopEventOnTime(const bool bViewBuider)
195.                    {
196.                            u_Interprocess Info;
197.                            int iPos, iTest, iCount;
198.                            
199.                            if (!m_Infos.bInit) ViewInfos();
200.                            iTest = 0;
201.                            while ((iTest == 0) && (!_StopFlag))
202.                            {
203.                                    iTest = (ChartSymbol(m_IdReplay) != "" ? iTest : -1);
204.                                    iTest = (GlobalVariableGet(def_GlobalVariableReplay, Info.df_Value) ? iTest : -1);
205.                                    iTest = (iTest == 0 ? (Info.s_Infos.isPlay ? 1 : iTest) : iTest);
206.                                    if (iTest == 0) Sleep(100);
207.                            }
208.                            if ((iTest < 0) || (_StopFlag)) return false;
209.                            AdjustPositionToReplay(bViewBuider);
210.                            Info.ServerTime = m_Ticks.Info[m_ReplayCount].time;
211.                            GlobalVariableSet(def_GlobalVariableServerTime, Info.df_Value);
212.                            iPos = iCount = 0;
213.                            while ((m_ReplayCount < m_Ticks.nTicks) && (!_StopFlag))
214.                            {
215.                                    iPos += (int)(m_ReplayCount < (m_Ticks.nTicks - 1) ? m_Ticks.Info[m_ReplayCount + 1].time_msc - m_Ticks.Info[m_ReplayCount].time_msc : 0);
216.                                    CreateBarInReplay(true);
217.                                    while ((iPos > 200) && (!_StopFlag))
218.                                    {
219.                                            if (ChartSymbol(m_IdReplay) == "") return false;
220.                                            GlobalVariableGet(def_GlobalVariableReplay, Info.df_Value);
221.                                            if (!Info.s_Infos.isPlay) return true;
222.                                            Info.s_Infos.iPosShift = (ushort)((m_ReplayCount * def_MaxPosSlider) / m_Ticks.nTicks);
223.                                            GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
224.                                            Sleep(195);
225.                                            iPos -= 200;
226.                                            iCount++;
227.                                            if (iCount > 4)
228.                                            {
229.                                                    iCount = 0;
230.                                                    GlobalVariableGet(def_GlobalVariableServerTime, Info.df_Value);
231.                                                    if ((m_Ticks.Info[m_ReplayCount].time - m_Ticks.Info[m_ReplayCount - 1].time) > 60) Info.ServerTime = ULONG_MAX; else
232.                                                    {
233.                                                            Info.ServerTime += 1;
234.                                                            Info.ServerTime = ((Info.ServerTime + 1) < m_Ticks.Info[m_ReplayCount].time ? Info.ServerTime : m_Ticks.Info[m_ReplayCount].time);
235.                                                    };
236.                                                    GlobalVariableSet(def_GlobalVariableServerTime, Info.df_Value);
237.                                            }
238.                                    }
239.                            }                               
240.                            return (m_ReplayCount == m_Ticks.nTicks);
241.                    }                               
242. //+------------------------------------------------------------------+
243. };
244. //+------------------------------------------------------------------+
245. #undef macroRemoveSec
246. #undef def_SymbolReplay
247. //+------------------------------------------------------------------+

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

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

Рисунок 07

Изображение 07. Ошибки, все еще присутствующие в Индикаторе

То есть, теперь нам нужно исправить код индикатора. При попытке это сделать, вы получите результат, как на изображении 08.

Рисунок 08

Изображение 08 — Ошибки, возникшие по той же причине

Еще раз, продолжайте пошагово. Всегда начинайте с первой указанной ошибки.

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.49"
07. #property link "https://www.mql5.com/ru/articles/11820"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. C_Terminal *terminal = NULL;
14. C_Controls *control = NULL;
15. //+------------------------------------------------------------------+
16. #define def_InfoTerminal (*terminal).GetInfoTerminal()
17. #define def_ShortName       "Market_" + def_SymbolReplay
18. //+------------------------------------------------------------------+
19. int OnInit()
20. {
21.     u_Interprocess Info;
22. 
23.     ResetLastError();
24.     if (CheckPointer(control = new C_Controls(terminal = new C_Terminal())) == POINTER_INVALID) return INIT_FAILED;
25.     if (_LastError != ERR_SUCCESS) return INIT_FAILED;
26.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
27.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device");
28.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
29.     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
30.     (*control).Init(Info.s_Infos.isPlay);
31.         
32.     return INIT_SUCCEEDED;
33. }
34. //+------------------------------------------------------------------+
35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
36. {
37.     static bool bWait = false;
38.     u_Interprocess Info;
39.     
40.     Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
41.     if (!bWait)
42.     {
43.             if (Info.s_Infos.isWait)
44.             {
45.                     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOn, 1, 0, "");
46.                     bWait = true;
47.             }
48.     }else if (!Info.s_Infos.isWait)
49.     {
50.             EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
51.             bWait = false;
52.     }
53.     
54.     return rates_total;
55. }
56. //+------------------------------------------------------------------+
57. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
58. {
59.     (*control).DispatchMessage(id, lparam, dparam, sparam);
60. }
61. //+------------------------------------------------------------------+
62. void OnDeinit(const int reason)
63. {
64.     switch (reason)
65.     {
66.             case REASON_REMOVE:
67.             case REASON_CHARTCLOSE:
68.                     if (def_InfoTerminal.szSymbol != def_SymbolReplay) break;
69.                     GlobalVariableDel(def_GlobalVariableReplay);
70.                     ChartClose(def_InfoTerminal.ID);
71.                     break;
72.     }
73.     delete control;
74.     delete terminal;
75. }
76. //+------------------------------------------------------------------+

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

Этот код уже полностью исправлен, но при попытке его скомпилировать, вы все равно будете получать некоторые ошибки от компилятора, которые можно увидеть на изображении 09.

Рисунок 09

Изображение 09 — Попытка скомпилировать контрольный индикатор

Теперь нам нужно перейти к заголовочному файлу C_Controls.mqh и внести исправления. Но эти исправления достаточно просты для выполнения, поскольку все, что нужно будет сделать, это удалить любые ссылки на u_Value. Таким образом, в конце этого процесса вы получите файл, код которого можно увидеть ниже:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\Interprocess.mqh"
005. //+------------------------------------------------------------------+
006. #define def_PathBMP                "Images\\Market Replay\\Control\\"
007. #define def_ButtonPlay             def_PathBMP + "Play.bmp"
008. #define def_ButtonPause            def_PathBMP + "Pause.bmp"
009. #define def_ButtonLeft             def_PathBMP + "Left.bmp"
010. #define def_ButtonLeftBlock        def_PathBMP + "Left_Block.bmp"
011. #define def_ButtonRight            def_PathBMP + "Right.bmp"
012. #define def_ButtonRightBlock       def_PathBMP + "Right_Block.bmp"
013. #define def_ButtonPin              def_PathBMP + "Pin.bmp"
014. #define def_ButtonWait             def_PathBMP + "Wait.bmp"
015. #resource "\\" + def_ButtonPlay
016. #resource "\\" + def_ButtonPause
017. #resource "\\" + def_ButtonLeft
018. #resource "\\" + def_ButtonLeftBlock
019. #resource "\\" + def_ButtonRight
020. #resource "\\" + def_ButtonRightBlock
021. #resource "\\" + def_ButtonPin
022. #resource "\\" + def_ButtonWait
023. //+------------------------------------------------------------------+
024. #define def_PrefixObjectName       "Market Replay _ "
025. #define def_NameObjectsSlider      def_PrefixObjectName + "Slider"
026. #define def_PosXObjects            120
027. //+------------------------------------------------------------------+
028. #include "..\Auxiliar\C_Terminal.mqh"
029. #include "..\Auxiliar\C_Mouse.mqh"
030. //+------------------------------------------------------------------+
031. #define def_AcessTerminal (*Terminal)
032. #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal()
033. //+------------------------------------------------------------------+
034. class C_Controls : protected C_Mouse
035. {
036.    protected:
037.            enum EventCustom {ev_WaitOn, ev_WaitOff};
038.    private :
039. //+------------------------------------------------------------------+
040.            string  m_szBtnPlay;
041.            bool            m_bWait;
042.            struct st_00
043.            {
044.                    string  szBtnLeft,
045.                            szBtnRight,
046.                            szBtnPin,
047.                            szBarSlider,
048.                            szBarSliderBlock;
049.                    int     posPinSlider,
050.                            posY,
051.                            Minimal;
052.            }m_Slider;
053.            C_Terminal *Terminal;
054. //+------------------------------------------------------------------+
055. inline void CreateObjectBitMap(int x, int y, string szName, string Resource1, string Resource2 = NULL)
056.                    {
057.                            ObjectCreate(def_InfoTerminal.ID, szName, OBJ_BITMAP_LABEL, 0, 0, 0);
058.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XDISTANCE, def_PosXObjects + x);
059.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_YDISTANCE, y);
060.                            ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_BMPFILE, 0, "::" + Resource1);
061.                            ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_BMPFILE, 1, "::" + (Resource2 == NULL ? Resource1 : Resource2));
062.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_ZORDER, 1);
063.                    }
064. //+------------------------------------------------------------------+
065. inline void CreteBarSlider(int x, int size)
066.                    {
067.                            ObjectCreate(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJ_RECTANGLE_LABEL, 0, 0, 0);
068.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
069.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Slider.posY - 4);
070.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
071.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
072.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
073.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
074.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
075.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
076. //---
077.                            ObjectCreate(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJ_RECTANGLE_LABEL, 0, 0, 0);
078.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
079.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Slider.posY - 9);
080.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
081.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
082.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
083.                    }
084. //+------------------------------------------------------------------+
085.            void CreateBtnPlayPause(bool state)
086.                    {
087.                            m_szBtnPlay = def_PrefixObjectName + "Play";
088.                            CreateObjectBitMap(0, 25, m_szBtnPlay, (m_bWait ? def_ButtonWait : def_ButtonPause), (m_bWait ? def_ButtonWait : def_ButtonPlay));
089.                            ObjectSetInteger(def_InfoTerminal.ID, m_szBtnPlay, OBJPROP_STATE, state);
090.                    }
091. //+------------------------------------------------------------------+
092.            void CreteCtrlSlider(void)
093.                    {
094.                            u_Interprocess Info;
095.                            
096.                            m_Slider.szBarSlider      = def_NameObjectsSlider + " Bar";
097.                            m_Slider.szBarSliderBlock = def_NameObjectsSlider + " Bar Block";
098.                            m_Slider.szBtnLeft        = def_NameObjectsSlider + " BtnL";
099.                            m_Slider.szBtnRight       = def_NameObjectsSlider + " BtnR";
100.                            m_Slider.szBtnPin         = def_NameObjectsSlider + " BtnP";
101.                            m_Slider.posY = 40;
102.                            CreteBarSlider(77, 436);
103.                            CreateObjectBitMap(47, 25, m_Slider.szBtnLeft, def_ButtonLeft, def_ButtonLeftBlock);
104.                            CreateObjectBitMap(511, 25, m_Slider.szBtnRight, def_ButtonRight, def_ButtonRightBlock);
105.                            CreateObjectBitMap(0, m_Slider.posY, m_Slider.szBtnPin, def_ButtonPin);
106.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnPin, OBJPROP_ANCHOR, ANCHOR_CENTER);
107.                            if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
108.                            m_Slider.Minimal = Info.s_Infos.iPosShift;
109.                            PositionPinSlider(Info.s_Infos.iPosShift);
110.                    }
111. //+------------------------------------------------------------------+
112. inline void RemoveCtrlSlider(void)
113.                    {                       
114.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, false);
115.                            ObjectsDeleteAll(def_InfoTerminal.ID, def_NameObjectsSlider);
116.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, true);
117.                    }
118. //+------------------------------------------------------------------+
119. inline void PositionPinSlider(int p, const int minimal = 0)
120.                    {
121.                            m_Slider.posPinSlider = (p < minimal ? minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
122.                            m_Slider.posPinSlider = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
123.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnPin, OBJPROP_XDISTANCE, m_Slider.posPinSlider + def_PosXObjects + 95);
124.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != minimal);
125.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != m_Slider.Minimal);
126.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnRight, OBJPROP_STATE, m_Slider.posPinSlider < def_MaxPosSlider);
127.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, minimal + 2);
128.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
129.                            ChartRedraw();
130.                    }
131. //+------------------------------------------------------------------+
132.    public  :
133. //+------------------------------------------------------------------+
134.            C_Controls(C_Terminal *arg)
135.                    :C_Mouse(arg),
136.                     m_bWait(false)
137.                    {
138.                            if (CheckPointer(Terminal = arg) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
139.                            m_szBtnPlay          = NULL;
140.                            m_Slider.szBarSlider = NULL;
141.                            m_Slider.szBtnPin    = NULL;
142.                            m_Slider.szBtnLeft   = NULL;
143.                            m_Slider.szBtnRight  = NULL;
144.                    }
145. //+------------------------------------------------------------------+
146.            ~C_Controls()
147.                    {
148.                            if (CheckPointer(Terminal) == POINTER_INVALID) return;
149.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, false);
150.                            ObjectsDeleteAll(def_InfoTerminal.ID, def_PrefixObjectName);
151.                    }
152. //+------------------------------------------------------------------+
153.            void Init(const bool state)
154.                    {
155.                            CreateBtnPlayPause(state);
156.                            GlobalVariableTemp(def_GlobalVariableReplay);
157.                            if (!state) CreteCtrlSlider();
158.                            ChartRedraw();
159.                    }
160. //+------------------------------------------------------------------+
161.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
162.                    {
163.                            u_Interprocess Info;
164.                            static int six = -1, sps;
165.                            int x, y, px1, px2;
166.                            
167.                            C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
168.                            switch (id)
169.                            {
170.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOn):
171.                                            if (lparam == 0) break;
172.                                            m_bWait = true;
173.                                            CreateBtnPlayPause(true);
174.                                            break;
175.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOff):
176.                                            if (lparam == 0) break;
177.                                            m_bWait = false;
178.                                            Info.df_Value = dparam;
179.                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
180.                                            break;
181.                                    case CHARTEVENT_OBJECT_DELETE:
182.                                            if (StringSubstr(sparam, 0, StringLen(def_PrefixObjectName)) == def_PrefixObjectName)
183.                                            {
184.                                                    if (StringSubstr(sparam, 0, StringLen(def_NameObjectsSlider)) == def_NameObjectsSlider)
185.                                                    {
186.                                                            RemoveCtrlSlider();
187.                                                            CreteCtrlSlider();
188.                                                    }else
189.                                                    {
190.                                                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
191.                                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
192.                                                    }
193.                                                    ChartRedraw();
194.                                            }
195.                                            break;
196.                                    case CHARTEVENT_OBJECT_CLICK:
197.                                            if (m_bWait) break;
198.                                            if (sparam == m_szBtnPlay)
199.                                            {
200.                                                    Info.s_Infos.isPlay = (bool) ObjectGetInteger(def_InfoTerminal.ID, m_szBtnPlay, OBJPROP_STATE);
201.                                                    if (!Info.s_Infos.isPlay) CreteCtrlSlider(); else
202.                                                    {
203.                                                            RemoveCtrlSlider();
204.                                                            m_Slider.szBtnPin = NULL;
205.                                                    }
206.                                                    Info.s_Infos.iPosShift = (ushort) m_Slider.posPinSlider;
207.                                                    GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
208.                                                    ChartRedraw();
209.                                            }else   if (sparam == m_Slider.szBtnLeft) PositionPinSlider(m_Slider.posPinSlider - 1);
210.                                            else if (sparam == m_Slider.szBtnRight) PositionPinSlider(m_Slider.posPinSlider + 1);
211.                                            break;
212.                                    case CHARTEVENT_MOUSE_MOVE:
213.                                            if (GetInfoMouse().ExecStudy) return;
214.                                            if ((CheckClick(C_Mouse::eClickLeft)) && (m_Slider.szBtnPin != NULL))
215.                                            {
216.                                                    x = GetInfoMouse().Position.X;
217.                                                    y = GetInfoMouse().Position.Y;
218.                                                    px1 = m_Slider.posPinSlider + def_PosXObjects + 86;
219.                                                    px2 = m_Slider.posPinSlider + def_PosXObjects + 114;
220.                                                    if ((y >= (m_Slider.posY - 14)) && (y <= (m_Slider.posY + 14)) && (x >= px1) && (x <= px2) && (six == -1))
221.                                                    {
222.                                                            six = x;
223.                                                            sps = m_Slider.posPinSlider;
224.                                                            ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, false);
225.                                                    }
226.                                                    if (six > 0) PositionPinSlider(sps + x - six);
227.                                            }else if (six > 0)
228.                                            {
229.                                                    six = -1;
230.                                                    ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, true);
231.                                            }
232.                                            break;
233.                            }
234.                    }
235. //+------------------------------------------------------------------+
236. };
237. //+------------------------------------------------------------------+
238. #undef def_InfoTerminal
239. #undef def_AcessTerminal
240. #undef def_PosXObjects
241. #undef def_ButtonPlay
242. #undef def_ButtonPause
243. #undef def_ButtonLeft
244. #undef def_ButtonRight
245. #undef def_ButtonPin
246. #undef def_NameObjectsSlider
247. #undef def_PrefixObjectName
248. #undef def_PathBMP
249. //+------------------------------------------------------------------+

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

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

Рисунок 10

Изображение 10: Финальная компиляция

Это означает, что сервис был успешно скомпилирован, и обратите внимание, что также был скомпилирован и индикатор. Что в свою очередь означает, что теперь можно просто удалить исполняемый файл индикатора с помощью проводника файлов, поскольку теперь он станет частью исполняемого файла сервиса репликации/моделирования. Однако если вы удалите исполняемый файл индикатора и попытаетесь запустить систему репликации/моделирования в MetaTrader 5, вы увидите, что график открывается, но контрольный индикатор не появляется. Почему?

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

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

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


Заключение

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

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

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

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

Прикрепленные файлы |
Anexo.zip (420.65 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Sergei Lebedev
Sergei Lebedev | 11 мая 2024 в 12:10

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

Прошу вас написать следующую статью #50 под названием "Руководство пользователя" и изложить в ней суть каждого компонента этого проекта - системы реплеев, советника и индикаторов.

Было бы здорово добавить несколько практических примеров использования, например:

- "Переигрывание и переторговка Brexit case на GBPUSD в июне 2016 года в образовательных целях";

- "Переигрывание и переторговывание кейса Gold-rush на XAUUSD в марте 2020 года в учебных целях";

Предполагается, что в этих практических примерах необходимо использовать извлечение исторических данных из реальных символов 'GBPUSD'/'XAUUSD' (с любого подключенного форекс-счета), создание пользовательских символов типа 'repGBPUSD'/'repXAUUSd' с использованием извлеченных данных в режиме воспроизведения, добавление некоторых общих индикаторов (RSI(14), MA(50) и т.д.) и предоставление пользователям опыта переторговки этих исторических событий в реальном времени.

Такое руководство пользователя с практическими примерами переторговки в реальном времени Brexit и Gold-rush будет действительно отличным завершением этого проекта!

Нейросети в трейдинге: Контрастный Трансформер паттернов Нейросети в трейдинге: Контрастный Трансформер паттернов
Контрастный Transformer паттернов осуществляет анализ рыночных ситуаций, как на уровне отдельных свечей, так и целых паттернов. Что способствует повышению качества моделирования рыночных тенденций. А применение контрастного обучения для согласования представлений свечей и паттернов ведет к саморегуляции и повышению точности прогнозов.
Возможности Мастера MQL5, которые вам нужно знать (Часть 21): Тестирование с данными экономического календаря Возможности Мастера MQL5, которые вам нужно знать (Часть 21): Тестирование с данными экономического календаря
Данные экономического календаря по умолчанию недоступны для тестирования с помощью советников в тестере стратегий. Мы рассмотрим, как базы данных могут помочь обойти это ограничение. В частности, мы увидим, как можно использовать базы данных SQLite для архивирования новостей Экономического календаря, чтобы советники, собранные с помощью Мастера, могли использовать их для генерации торговых сигналов.
Разработка системы репликации (Часть 50): Все усложняется (II) Разработка системы репликации (Часть 50): Все усложняется (II)
Мы решим проблему ID графиков, но в то же время начнем обеспечивать пользователю возможность использования личного шаблона, ориентированного на анализ того актива, который он хочет изучить и смоделировать. Представленные здесь материалы носят исключительно дидактический характер, ни в коем случае нельзя рассматривать их как приложение с никакой иной целью, кроме изучения и освоения представленных концепций.
Нейронная сеть на практике: Первый нейрон Нейронная сеть на практике: Первый нейрон
В этой статье мы начнем создавать нечто простое и скромное: нейрон. Мы запрограммируем его с помощью очень небольшого кода на MQL5. Нейрон прекрасно работал в тех тестах, которые я проводил. Вернемся немного назад в этой серии статей о нейронных сетях, чтобы понять, о чем я говорю.