English 中文 Español Deutsch 日本語 Português
preview
Разработка системы репликации - Моделирование рынка (Часть 08): Блокировка индикатора

Разработка системы репликации - Моделирование рынка (Часть 08): Блокировка индикатора

MetaTrader 5Примеры | 29 августа 2023, 17:27
510 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье, Разработка системы репликации - Моделирование рынка (Часть 07): Первые улучшения (II), мы внесли некоторые исправления и корректировки. Однако ошибка всё же была, как показано на прикрепленном к этой статье видео.

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

Я предлагаю вам продолжить чтение, ведь содержание обещает быть весьма полезным.


Блокировка индикатора на конкретном активе.

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

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
#include <Market Replay\C_Controls.mqh>
//+------------------------------------------------------------------+
C_Controls      Control;
//+------------------------------------------------------------------+
int OnInit()
{
        u_Interprocess Info;

        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
        if (_Symbol != def_SymbolReplay)
        {
                ChartIndicatorDelete(ChartID(), 0, def_ShortName);
                return INIT_FAILED;
        }
        if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.Value = 0;
        Control.Init(Info.s_Infos.isPlay);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Control.DispatchMessage(id, lparam, dparam, sparam);
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        switch (reason)
        {
                case REASON_REMOVE:
                case REASON_CHARTCLOSE:
                        if (_Symbol != def_SymbolReplay) break;
                        GlobalVariableDel(def_GlobalVariableReplay);
                        ChartClose(ChartID());
                        break;
        }
}
//+------------------------------------------------------------------+

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

Теперь один важный момент: при удалении его с графика MetaTrader 5 генерирует событие DeInit. Данное событие запускает функцию OnDeInit при условии события REASON_REMOVE, указывающего на удаление индикатора с графика. Это связано с тем, что актив не совпадает с тем, для использования которого был разработан индикатор. Если мы не осуществим повторную проверку и не предотвратим запуск кода, график актива закроется. Однако благодаря нашей проверке он останется открытым.

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

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

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


Мы должны избегать использования нескольких индикаторов на одном графике.

Мы решили одну проблему, теперь давайте займемся другой. Здесь представлены различные решения, зависящие от того, что мы действительно хотим и готовы сделать. Лично я не вижу идеального и окончательного решения данного вопроса. Однако я попытаюсь представить подход, с которым читатель сможет ознакомиться и разобраться в нем. Самое главное, что решение будет основано исключительно на MQL5. Я даже рассматривал возможность использования внешнего написания кода, но решил использовать только MQL5. Идея прибегнуть к внешнему написанию кода и использованию DLL для блокировки заманчива, но это было бы слишком легко.

Я думаю, нам нужно еще многое изучить в MQL5, прежде чем прибегать к внешней DLL для заполнения тех пробелов, которые не устраняет язык MQL5. Это обеспечит решение, которое будет выглядеть «чище» при использовании внешнего кода. Однако использование данной стратегии не покажет глубокого знания MQL5. Кроме того, это может укрепить ложное представление о том, что MetaTrader 5 является ограниченной платформой. Непонимание и недостаточное использование платформы подпитывают данное ложное представление.

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

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalVariableReplay        "Replay Infos"
#define def_GlobalVariableIdGraphics    "Replay ID"
#define def_SymbolReplay                "RePlay"
#define def_MaxPosSlider                400
#define def_ShortName                   "Market Replay"
//+------------------------------------------------------------------+
union u_Interprocess
{
        union u_0
        {
                double  df_Value;
                long    IdGraphic;
        }u_Value;
        struct st_0
        {
                bool    isPlay;
                int     iPosShift;
        }s_Infos;
};
//+------------------------------------------------------------------+

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

ПРИМЕЧАНИЕ: Было бы интересно, если бы разработчики платформы MetaTrader 5 и языка MQL5 предоставили сервису возможность добавлять индикатор на конкретный график или разрешили сервису вызывать и выполнять скрипт на графике. С помощью скриптов мы можем добавить индикатор на конкретный график, но с сервисом это пока невозможно. Мы можем открыть график, но мы не можем добавить к нему индикатор. При попытке выполнить это действие всегда выдается сообщение об ошибке, даже если используются функции MQL5.  На момент написания этих строк MetaTrader 5 находится на версия 5.00 билд 3280.

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

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

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart() 
{ 
  
        ENUM_TIMEFRAMES time = PERIOD_D1;
        string szSymbol = "EURUSD";
        long id = ChartOpen(szSymbol, time);
        ChartRedraw(id);

        ChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));
}

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

#property service
//+------------------------------------------------------------------+
void OnStart()
{
        ENUM_TIMEFRAMES time = PERIOD_D1;
        string szSymbol = "EURUSD";
        long id = ChartOpen(szSymbol, time);
        ChartRedraw(id);

        ChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));
}

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

Даже если мы запретим, как показано выше, добавлять индикатор на график, который не связан с активом репликации, мы сможем вставить индикатор на график, активом которого является репликация рынка. Этого нельзя допустить. После изменений в заголовочном файле Interprocess.mqh, давайте сосредоточимся на коде сервиса, а точнее на заголовочном файле C_Replay.mqh.

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

long ViewReplay(ENUM_TIMEFRAMES arg1)
{
        u_Interprocess info;
                        
        if ((m_IdReplay = ChartFirst()) > 0) do
        {
                if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
                {
                        ChartClose(m_IdReplay);
                        ChartRedraw();
                }
        }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
        info.u_Value.IdGraphic = m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
        ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");
        ChartRedraw(m_IdReplay);
        GlobalVariableDel(def_GlobalVariableIdGraphics);
        GlobalVariableTemp(def_GlobalVariableIdGraphics);
        GlobalVariableSet(def_GlobalVariableIdGraphics, info.u_Value.df_Value);
        return m_IdReplay;
}

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

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

void CloseReplay(void)
{
        ArrayFree(m_Ticks.Info);
        ChartClose(m_IdReplay);
        SymbolSelect(def_SymbolReplay, false);
        CustomSymbolDelete(def_SymbolReplay);
        GlobalVariableDel(def_GlobalVariableReplay);
        GlobalVariableDel(def_GlobalVariableIdGraphics);
}

Благодаря этим изменениям мы можем добавить новый элемент управления в индикатор.

int OnInit()
{
        u_Interprocess Info;

        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
        if ((_Symbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics)))
        {
                ChartIndicatorDelete(ChartID(), 0, def_ShortName);
                return INIT_FAILED;
        }
        if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
        Control.Init(Info.s_Infos.isPlay);
        
        return INIT_SUCCEEDED;
}

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

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

int OnInit()
{
#define macro_INIT_FAILED { ChartIndicatorDelete(ChartID(), 0, def_ShortName); return INIT_FAILED; }
        u_Interprocess Info;

        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
        if ((_Symbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics))) macro_INIT_FAILED;
        Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
        if (Info.u_Value.IdGraphic != ChartID()) macro_INIT_FAILED;
        if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
        Control.Init(Info.s_Infos.isPlay);
        
        return INIT_SUCCEEDED;
        
#undef macro_INIT_FAILED
}

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

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



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

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

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

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

Чтобы понять, что мы собираемся делать на самом деле, необходимо понять текущие ограничения языка, при которых абстракция определенной информации невозможна. Обратите внимание на этот момент: Я НЕ СКАЗАЛ, что это сделать невозможно, я имею в виду, что мы не можем создать абстракцию, облегчающую нашу работу. Это разные понятия. Одно дело развивать абстракцию данных и информации, а другое — иметь возможность манипулировать различными данными так, как это нужно нам: нельзя смешивать эти понятия.

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

union u01
{
        
double  value;
        struct st
        {
                ulong info : 63;
                bool signal;
        }c;
}data;

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

struct st
{
        bool PlayPause;
        bool Reservad : 6;
        bool RS_Info;
}ctrl;

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

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

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

#property copyright "Daniel Jose"
#property description "This indicator cannot be used\noutside of the market replay service."
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
#include <Market Replay\C_Controls.mqh>
//+------------------------------------------------------------------+
C_Controls      Control;
//+------------------------------------------------------------------+
#define def_BitShift ((sizeof(ulong) * 8) - 1)
//+------------------------------------------------------------------+
int OnInit()
{
#define macro_INIT_FAILED { ChartIndicatorDelete(id, 0, def_ShortName); return INIT_FAILED; }
        u_Interprocess Info;
        long id = ChartID();
        ulong ul = 1;

        ul <<= def_BitShift;
        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
        if ((_Symbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics))) macro_INIT_FAILED;
        Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
        if (Info.u_Value.IdGraphic != id) macro_INIT_FAILED;
        if ((Info.u_Value.IdGraphic >> def_BitShift) == 1) macro_INIT_FAILED;
        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device");
        Info.u_Value.IdGraphic |= ul;
        GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value); 
        if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
        Control.Init(Info.s_Infos.isPlay);
        
        return INIT_SUCCEEDED;
        
#undef macro_INIT_FAILED
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Control.DispatchMessage(id, lparam, dparam, sparam);
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        u_Interprocess Info;
        ulong ul = 1;

        switch (reason)
        {
                case REASON_CHARTCHANGE:
                        ul <<= def_BitShift;
                        Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
                        Info.u_Value.IdGraphic ^= ul;
                        GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value);
                        break;
                case REASON_REMOVE:
                case REASON_CHARTCLOSE:
                        if (_Symbol != def_SymbolReplay) break;
                        GlobalVariableDel(def_GlobalVariableReplay);
                        ChartClose(ChartID());
                        Control.Finish();
                        break;
        }
}
//+------------------------------------------------------------------+

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

Если присмотреться, то вы заметите, что типу double требуется 8 байтов для хранения своего значения. Аналогично, тип long (со знаком) или ulong (без знака) также занимает те же 8 байт. Учитывая, что идентификатор графика, полученный через ChartID, возвращает нам тип long, у нас 1 бит лишний, который как раз и используется для обозначения знака. Что мы сделаем - будем использовать именно этот бит для установки индикатора на график. И будем манипулировать именем индикатора, чтобы предотвратить добавление другого индикатора управления на тот же график. Как? Необходимо дать объяснение.

Сначала мы определяем, сколько бит у нас будет и с какими мы будем работать. Так, независимо от того, используем ли мы 64-битную, 32-битную или 128-битную систему, это определение будет использовать соответствующий тип и длину. Хоть мы и знаем, что он 64-битный, я хочу, чтобы настройки были не статическими, а гибкими. Поэтому мы вычитаем 1 из этого значения, таким образом изолируя знаковый бит типа long.

Далее активируем младший бит из этих 64 бит. Сделав это, мы получим значение 1, которое и будет нашей отправной точкой. Далее выполняем 63-битный сдвиг влево, в результате чего получается значение 0x800000000000000000000000, где старший бит имеет значение, равное 1, то есть является true. Этого шага можно было бы избежать, если бы мы установили данное значение напрямую, но риск ошибиться при его вводе высок. Действуя таким образом, мы минимизируем ​​возможность ошибки.

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

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

Info.u_Value.IdGraphic &= (~ul);

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

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

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

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

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

Я хотел бы сказать, что это шутка, но это не так. На самом деле, избежать данной ситуации вам не удастся. Это связано с «ошибкой» (обратите внимание на кавычки) в системе. Я не уверен, что именно является причиной этого и как это могло произойти, но взгляните на следующий код:

long ViewReplay(ENUM_TIMEFRAMES arg1)
{
        u_Interprocess info;
                                
        if ((m_IdReplay = ChartFirst()) > 0) do
        {
                if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
                {
                        ChartClose(m_IdReplay);
                        ChartRedraw();
                }
        }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
        info.u_Value.IdGraphic = m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
        ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");
        ChartRedraw(m_IdReplay);
        GlobalVariableDel(def_GlobalVariableIdGraphics);
        GlobalVariableTemp(def_GlobalVariableIdGraphics);
        GlobalVariableSet(def_GlobalVariableIdGraphics, info.u_Value.df_Value);
        return m_IdReplay;
}

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

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

<indicator>
name=Custom Indicator
path=Indicators\Market Replay.ex5
apply=0
show_data=1
scale_inherit=0
scale_line=0
scale_line_percent=50
scale_line_value=0.000000
scale_fix_min=0
scale_fix_min_val=0.000000
scale_fix_max=0
scale_fix_max_val=0.000000
expertmode=0
fixed_height=-1


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

Исправленная версия показана ниже:

<indicator>
name=Custom Indicator
path=Indicators\Market Replay.ex5
apply=1
show_data=1
scale_inherit=0
scale_line=0
scale_line_percent=50
scale_line_value=0.000000
scale_fix_min=0
scale_fix_min_val=0.000000
scale_fix_max=0
scale_fix_max_val=0.000000
expertmode=0
fixed_height=-1

Я предпочел не подчеркивать, что именно изменилось; я хочу, чтобы сам читатель нашел и попытался понять это. Но будьте уверены, что в приложенной версии кода система уже будет работать правильно.


Заключение

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

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




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

Прикрепленные файлы |
Market_Replay.zip (13058.5 KB)
Делаем информационную панель для отображения данных в индикаторах и советниках Делаем информационную панель для отображения данных в индикаторах и советниках
В статье рассмотрим создание класса информационной панели для использования её в индикаторах и советниках. Это вводная статья в небольшой серии статей с шаблонами подключения и использования стандартных индикаторов в советниках. Начнем мы с создания панели — аналога окна данных MetaTrader 5.
Нейросети — это просто (Часть 56): Использование ядерной нормы для стимулирования исследования Нейросети — это просто (Часть 56): Использование ядерной нормы для стимулирования исследования
Исследование окружающей среды в задачах обучения с подкреплением является актуальной проблемой. Ранее мы уже рассматривали некоторые подходы. И сегодня я предлагаю познакомиться с ещё одним методом, основанным на максимизации ядерной нормы. Он позволяет агентам выделять состояния среды с высокой степенью новизны и разнообразия.
Разработка системы репликации - Моделирование рынка (Часть 09): Пользовательские события Разработка системы репликации - Моделирование рынка (Часть 09): Пользовательские события
Здесь мы увидим, как активировать пользовательские события и проработать вопрос о том, как индикатор сообщает о состоянии сервиса репликации/моделирования.
Разработка системы репликации - Моделирование рынка (Часть 07): Первые улучшения (II) Разработка системы репликации - Моделирование рынка (Часть 07): Первые улучшения (II)
В предыдущей статье мы внесли исправления в некоторые моменты и добавили тесты в нашу систему репликации для обеспечения максимально возможной стабильности. Мы также начали создавать и использовать конфигурационный файл для данной системы.