English Русский 中文 Español Deutsch 日本語
preview
Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 08): Travando o Indicador

Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 08): Travando o Indicador

MetaTrader 5Exemplos | 28 abril 2023, 10:49
567 0
Daniel Jose
Daniel Jose

Introdução

No artigo, Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 07): Primeiras melhorias (II), fizemos alguns ajustes e correções, mas ainda assim ficou uma falha, que você pode ver no vídeo presente aquele artigo.

Aqui vamos corrigir aquela falha, apesar de parecer se algo simples de corrigir. Você verá que será preciso fazer diversas coisas. De certa forma isto será bem interessante, ao mesmo tempo que bastante curioso de ser feito. Iremos criar uma forma, de garantir que um indicador seja aplicado apenas a um único gráfico, e em um ativo especifico. Mesmo quando o usuário venha a tentar, e de forma insistente, ele não conseguirá colocar o indicador, em um gráfico diferente, ou abrir ele, mais de uma vez dentro da mesma seção.

Então acompanhe o artigo, pois a coisa vai ser bem interessante.


Travando o indicador a um ativo especifico.

A primeira coisa que faremos, será travar o indicador de controle, ao ativo usado para fazer o replay de mercado. De certa forma é algo muito simples de ser feito, mas já ajudará bem no resto da nossa tarefa principal. Veja como o código do indicador, ficará neste caso:

#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;
        }
}
//+------------------------------------------------------------------+

A primeira coisa que fazemos, é testar se o ativo é ou não, o ativo usado como replay de mercado. Caso ele não seja, o indicador simplesmente será encerrado. Reparem que precisamos saber, qual é o nome do indicador. Por conta disto a primeira rotina a ser de fato executada, na inicialização, será a que dá um nome para nosso indicador, somente assim poderemos removê-lo sem mais dificuldades.

Agora prestem bastante atenção a um detalhe: Como nosso indicador irá ser removido do gráfico, o MetaTrader 5, irá gerar um evento DeInit. Este irá disparar uma chamada a função OnDeInit, sobre a condição de um evento REASON_REMOVE, que indica que a chamada, é pelo fato de que o nosso indicador estará sendo removido do gráfico. Acontece que ele estará sendo removido por conta de que o ativo, não é o que o indicador de fato foi projetado para usar. Caso não façamos um novo teste, e impeçamos que todo o código seja executado, o gráfico do ativo irá ser fechado. Mas por conta do teste, ele irá continuar aberto.

Não estranhem o fato do código do indicador, estar diferente do código disponível no artigo anterior. Lá o foco de melhorias e correções foram outros. Mas ao finalizar o artigo e ter terminado o código, fui fazer o vídeo, que se encontra naquele artigo, e acabei percebendo, que apesar de ter corrigido, um dos problemas que existia, havia outro que tinha passado batido. Desta forma, o código teve que ser modificado.

Apesar de tudo, não irei comentar aqui, todos os pontos que formam mudados. Já que uma parte considerável, teve que ser retirada, por não se mostrar tão eficaz, em promover o travamento, que é necessário e será implementado aqui. Por isto, o código acima, é bem diferente daquele. Mas acredito que o aprendizado deixado naquele artigo, pode vim a ser útil, para alguém em algum momento. Por este motivo o artigo foi mantido, pois serve também para mostrar, que não somos perfeitos. Mas devemos tentar nos adequar de maneira a fazer as coisas corretamente.

Com isto, nos criamos a primeira parte da trava, que precisamos para que o indicador de controle, exista apenas dentro do gráfico do ativo de replay de mercado. No entanto, o fato de ter feito isto, não impede, que você possa colocar mais de um indicador. Seja no mesmo gráfico, ou em gráficos diferentes, e isto precisa ser corrigido.


Evitando que tenhamos mais de um indicador no mesmo gráfico.

Agora que resolvemos um dos problemas, vamos resolver um outro. E este tem soluções distintas, dependendo do que você queira de fato fazer, e aceite fazer. Mas pessoalmente, não vejo uma forma de se chegar a uma solução ideal e definitiva, para se fazer este tipo de coisa. Mas vou tentar mostrar, uma forma, com a qual você conseguirá conviver e entender. Mas o principal, é que a solução usará apenas e somente o MQL5. Se bem que cogitei utilizar uma solução envolvendo codificação externa, mas no final, decidi utilizar somente o MQL5. Por mais tentador que fosse, fazer uma codificação externa, e usar assim uma DLL para promover este travamento, isto não seria tão desafiador.

Acredito que ainda podemos explorar muito o MQL5, antes que, de fato tenhamos que fazer uso de uma DLL externa, para cobrir alguns vãos, que a linguagem MQL5 não cobre. Assim promover uma solução aparentemente mais "limpa", ao usar um código externo. Mas fazer isto, não indicaria um completo entendimento do MQL5. Além de dar a entender, que não podemos fazer algo na linguagem ou na plataforma.  Talvez dai venha a ideia, que muitos divulgam erroneamente, de que o MetaTrader 5, é uma plataforma limitada. O simples fato, de não entende-la e subutiliza-la, faz com que esta falsa ideia tomem força.

Para implementar esta solução, teremos que fazer algumas mudanças e desfazer outras. A primeira coisa de fato a fazer: É modificar o arquivo de cabeçalho InterProcess.mqh, para que ele tenha a seguinte estrutura

#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;
};
//+------------------------------------------------------------------+

Parece ser algo um pouco bizarro, para quem não compreende muito de programação. Mas por incrível que pareça, a união acima, utiliza apenas 8 bytes de comprimento. Você já deve ter percebido, que nesta estrutura existia uma variável, que era usada no artigo anterior, e agora ela já não está ai. O motivo para isto, é que agora, não utilizaremos mais aquele modelo de travamento. Vamos usar um outro. Levemente mais complicado, mas ao mesmo tempo, bem mais eficiente na questão de travar o indicador de controle, a apenas e somente a um único gráfico. Este será bastante especifico, e será indicado, pelo serviço de replay.

NOTA: Até seria interessante, os desenvolvedores da plataforma MetaTrader 5, e dá linguagem MQL5, dessem ao serviço a possibilidade de adicionar um indicador. Isto um gráfico especifico, ou permitir que um serviço, pode-se chamar e executar um script em um gráfico. Já que utilizando scripts, podemos adicionar um indicador em um gráfico especifico, mas usando um serviço isto não é possível. Apesar de podermos abrir um gráfico, não conseguimos adicionar nenhum indicador neste gráfico. Mesmo que façamos uso das funções do MQL5, é sempre reportado algum tipo de erro, ao tentar executar este tipo de coisa.  No momento em que este artigo esta sendo escrito, o MetaTrader 5, se encontra na versão 5.00 build 3280.

Uma observação importante: No atual momento, em que os artigos estão sendo escritos, isto em uma fase bem mais avançada, consegui fazer tal coisa. Porém no momento em que este estava sendo escrito, não consegui encontrar nenhum tipo de referência, que pudesse me ajudar nesta questão. Então acompanhe esta sequência sobre o replay / simulador, para entender com eu encontrei a solução.

Nesta, se você executar o script abaixo, conseguirá abrir e adicionar um indicador no gráfico

//+------------------------------------------------------------------+
//| 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"));
}

Mas se você transformar este mesmo script, em um serviço. Você não conseguirá o mesmo efeito.

#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"));
}

Notem, que a única coisa, que foi modificada aqui, é a propriedade de compilação, que agora indica que o código compilado será um serviço. O simples fato de transformar um script em um serviço, usado uma palavra reservada, muda completamente o funcionamento do código. Mesmo que ele execute o mesmo trabalho que era feito antes. Por conta disto, teremos que utilizar um template, de forma a adicionar um indicador no gráfico. Se fosse possível adicionar um indicador via serviço, poderíamos compilar o indicador, como sendo um recurso dentro do serviço. Quando o gráfico fosse aberto, este irá receber o indicador diretamente pelo serviço. Não seria preciso que o indicador ficasse em meio a outros indicadores.

Mesmo que você impeça, conforme foi mostrado acima, que o indicador seja adicionado em um gráfico, que não tenha nenhum tipo de vinculo com o ativo de replay. Ainda assim, o usuário poderá adicionar o indicador em um gráfico, que tenha como ativo o replay de mercado, e não podemos permitir isto. Desta maneira, uma vez que fizemos a mudança no arquivo de cabeçalho Interprocess.mqh, vamos partir para o código de serviço. Para ser mais exato. Vamos para o código do arquivo de cabeçalho C_Replay.mqh.

Basicamente, vejam o que iremos fazer: Iremos informar ao indicador, se o serviço esta ou não executando. Quando ele estiver, vamos informar qual o gráfico principal. Para fazer isto, temos que modificar o código, de forma que agora, ele ficará conforme mostrado abaixo:

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;
}

O que fizemos aqui, foi primeiro deletar a variável global de terminal. Logo depois, criamos a mesma variável. Mas agora teremos a garantia de que ela será temporária.  Gravamos o valor da Id do gráfico, que o serviço abriu nesta variável. Com isto já teremos uma grande redução de nosso trabalho, dentro do indicador. Pois tudo que teremos que fazer, é analisar este valor gravado pelo serviço.

Mas você não pode se esquecer, de que ao encerrar o serviço, também remover esta variável global extra que criamos. E isto é feito, no código abaixo:

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

Com estas modificações. Agora podemos adicionar um novo teste no indicador.

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;
}

Mesmo que você tente colocar o indicador de controle, no ativo de replay de mercado, você não irá conseguir fazer isto, sem que o serviço de replay, tenha criado a variável global de terminal. Somente quando esta variável estiver presente, é que o indicador poderá ser adicionado. Mesmo no gráfico do ativo de replay. Mas o fato de fazer isto, ainda não resolve o nosso problema. Temos que fazer mais alguns testes.

Agora vamos fazer um teste, que irá começar a travar o indicador ao gráfico correto. A primeira fase é vista logo abaixo:

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
}

Por conta que iremos repetir muitas vezes, o mesmo código dentro do procedimento de inicialização, prefiro definir uma macro para nos ajudar. Assim evitar que venhamos a cometer algum erro de digitação, ou que venhamos a esquecer algum detalhe. Agora vamos a primeira fase de travamento. Quando o gráfico do ativo é criado, temos a passagem via variável global de terminal, o valor da ID deste gráfico. Então podemos capturar este valor, para assim, confirmar se o indicador de controle, está ou não, dentro do gráfico esperado.  Este deverá ser, o que foi criado pelo serviço de replay. Caso você esteja tentando adicionar o indicador, em um gráfico diferente, do que o gráfico criado pelo serviço de replay, esta tentativa será negada.

Apesar de isto, resolver uma enorme gama de situações. Ainda temos um problema: O de que podemos adicionar, indicadores extras de controle, dentro do mesmo gráfico. Para finalmente, resolver este último problema, iremos ter que fazer algo, um tanto quanto inusitado. Lembrando novamente, no momento que este artigo esta sendo escrito, a plataforma se encontra na versão mostrada na imagem abaixo:



Pode ser que no momento que você esteja lendo isto, a mesma já tenha sofrido mudanças suficientes, para que o mecanismo usado aqui, já esteja completamente obsoleto. Mas então, vamos ver o mecanismo que permite travar o indicador de controle. De forma que você não consiga adicionar novos indicadores de controle, no mesmo gráfico. Ao mesmo tempo, que trava ele em um gráfico especifico. Caso o indicador seja removido, o gráfico será fechado, e o serviço de replay será encerrado junto.

Antes de partirmos para o código em si. Devo ressaltar que o mecanismo, que irei utilizar, irá fazer o uso de uma lógica booleana. Se você não entende sobre estes conceitos, é bom dar uma pesquisada sobre o assunto. Já que eles são a base de todo código possível, e passivo de ser criado e desenvolvido. Muitos de vocês poderiam imaginar, que o caminho mais fácil, seria utilizar uma DLL, de forma a gerenciar a situação, de uma forma mais simples. De certa forma, devo concordar, mas qual seria a graça, em se criar uma solução, em um programa externo ?!?!

Isto não nos traria a completa e correta compreensão, de até onde vai os limites da linguagem MQL5, e quais deveriam ser as sugestões para que ela melhora-se. Vejo muita gente que diz que a linguagem C/C++, é a mais poderosa das linguagens, e isto é fato. Mas ela não nasceu pronta, foi crescendo e recebendo novas possibilidades, conforme os desenvolvedores iam utilizando ela, até o seu limite de desenvolvimento. Quando este limite era alcançado, e ainda assim não era possível implementar a funcionalidade que se desejava. Novas funções eram desenvolvidas, de forma a tornar a implementação de novos projetos antes impossíveis, finalmente possíveis. Foi assim que C/C++, se tornou uma linguagem tão poderosa, a ponto de quase,  para não dizer que tudo, pode ser escrito nela.

Vejo que o MQL5, tem as mesma qualidades e possibilidades que C/C++. É tudo uma questão de as pessoas tentem e experimentem a linguagem MQL5, até o seu limite. De forma, que quando estes limites sejam alcançados, estes desenvolvedores venham a sugerir melhorias, e novas funcionalidades ao MQL5. Assim com o tempo, ela se torná-la uma linguagem extremamente poderosa, para construção e desenvolvimento de aplicações voltadas para o MetaTrader 5.

Para entender o que vamos de fato fazer, é preciso entender uma limitação, que atualmente existe na linguagem. De forma, que a abstração de algumas informações, não são possíveis. Atenção a este detalhe: Eu NÃO DISSE que não é possível fazer, eu disse que não podemos criar, uma abstração, de forma a facilitar a nossa vida. São coisas diferentes. Uma é você conseguir desenvolver a abstração dos dados e informações. A outra é ser ou não possível, manipular os dados da forma como queremos. Não confunda as coisas.

Pois bem, em C/C++, conseguimos criar uma abstração de dados, a ponto de poder isolar 1 bit em particular. E isto, dentro de uma cadeia de bits. Este tipo de coisa, é feita de forma bastante simples. Por exemplo:

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

Apesar de parecer estranho e bobagem. Aqui estamos criando um tipo de abstração de dados, que nos permite saber, e até mudar o sinal da informação. É verdade que este código, não tem muita utilidade, aqui e agora. Mas pensem em algo um pouco diferente. Vamos supor, que estejamos querendo enviar informações. E vamos usar bits para controlar alguma coisa. Poderíamos ter algo do tipo mostrado abaixo:

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

Neste caso, o nível de abstração nos ajudaria a separar os bits, que realmente desejamos acessar, e isto de forma, que o código seria simples de ser lido. Mas como foi dito, no atual estágio de desenvolvimento do MQL5, não conseguimos utilizar este nível de abstração, mostrados acima. Temos que utilizar algo um pouco diferente, ou seja, a abstração, não pode ser criada. Mas podemos manipular os dados, se entendermos os limites da linguagem. Por conta disto, somos forçados a usar a lógica booleana, para conseguir manipular os dados, que seriam feitos, com o uso da abstração. No entanto, o fato de usar lógica booleana, torna o programa mais complexo de ser entendido. Saímos de um sistema de alto nível, usando abstrações, e entramos em um sistema de baixo nível, onde a abstração é forçada ao nível booleano.

No futuro, irei voltar a esta mesma questão. Mas você verá que o motivo será bem mais justificado, do que este mostrado agora. Tudo isto que estou falando, parece uma tremenda de uma bobagem. Mas vamos ver o código final, do indicador de controle. Você irá acabar entendendo, o que estou tentando mostrar, ao explicar, que muitas das vezes, não é que o MQL5 não consiga fazer as coisas. Na verdade é o fato de que muitos programadores, não desejarem descer a um nível mais baixo, onde a abstração de dados, simplesmente não existe, que torna impossível fazer ou criar certas coisas.

Abaixo vemos o código completo e na integra do indicador de controle, no atual estágio de desenvolvimento. Neste se torna possível travar o indicador em um gráfico, e impedir que o usuário adicione outros indicadores de controle, na mesma sessão do 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;
        }
}
//+------------------------------------------------------------------+

Não esta conseguindo entender o que esta de fato acontecendo ?!?! A ponto de o indicador de controle, venha a funcionar da forma como expliquei ?!?! Pois bem, é justamente este o problema. Neste código não existe a abstração, que tanto ajudaria a permitir que você entendesse ele, de forma simples e fácil. E isto, pelo simples fato de o MQL5, não nos permitir ter o mesmo nível de abstração, que podemos conseguir utilizando C/C++. Por conta disto, somos forçados a usar a lógica booleana, que apesar de ser mais complexa de entender, nos permite fazer a manipulação dos dados, de forma a conseguir os mesmos resultados, que seriam conseguidos via abstração.

Se você observar, verá que um tipo double, irá ocupar 8 bytes para conter o seu valor. Da mesma forma um tipo long ( com sinal ), ou ulong ( sem sinal ), ocupará também mesmos 8 bytes. Já que a id do gráfico, que conseguimos saber via ChartID, nos devolve um tipo long, ficamos com 1 bit sobrando, que é justamente o bit usado para indicar o sinal. O que fazemos aqui, é utilizar exatamente este bit, para conseguir travar o indicador no gráfico. E usamos a manipulação do nome do indicador, para impedir que outro indicador de controle, seja colocado no mesmo gráfico. Como ?!?! Acompanhem a explicação.

Primeiro, criamos uma definição para saber quantos bits teremos e estaremos tratando. Desta forma, não importa se estamos usando um sistema de 64 bits, 32 bits ou 128 bits ... esta definição, irá usar o tipo e o comprimento correto. Se bem que já sabemos que se trata de 64 bits. Mas não quero deixar a coisa estática, quero que ela seja adaptativa. Assim subtraímos 1 deste valor, desta maneira estaremos isolando o bit de sinal do tipo long,

Agora, pegamos e ligamos o bit menos significativo, destes 64 bits. Ao fazer isto, temos como valor 1, e este será o nosso ponto inicial. Uma vez feito isto, formamos um deslocamento para a esquerda em 63 bits, ou seja, agora iremos ter o valor 0x8000000000000000, que é o valor onde o bit mais significativo estará com o seu valor igual a 1, ou seja, verdadeiro. Este passo poderia ser substituído, se fosse criada uma definição com este valor, mas a probabilidade de se cometer um erro na digitação, é muito grande. Fazendo assim, reduzimos esta probabilidade.

Com este valor em mãos, temos dois caminhos distintos. O primeiro será travar o sistema. Já o segundo, é destravar o sistema, de forma que possamos permitir que a plataforma MetaTrader 5, reaplique o indicador no gráfico, assim que for necessário. Já que esta segunda parte é mais simples, vamos vê-la primeiro.

Então para destravar o sistema, iremos pegar o valor da variável global de terminal, que contem a ID do gráfico, e fazer uma operação XOR, neste valor, de forma que todos os bits serão mantidos, exceto o bit mais significativo. De certa forma, uma operação XOR não é o ideal, o melhor seria fazer uma operação NOT, seguida de uma operação AND, esta seria a forma ideal. Assim iriamos remover, qualquer informação contida no bit mais significativo. Mas já que a chamada se dará apenas e somente quando de fato, este bit já contiver alguma informação, não vejo problemas em usar uma operação XOR. Mas caso você tenha problemas, mude esta operação XOR para a linha mostrada abaixo:

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

De qualquer forma, teremos o que desejamos, que é o bit mais significativo sendo zerado. Então podemos armazenar o valor novamente na variável global de terminal, antes que o MetaTrader 5, tente repor o indicador de controle novamente no gráfico. Com isto, concluímos a parte fácil do sistema de trava. Agora vamos a parte realmente complicada.

Nesta parte, a primeira coisa a ser feita, é testar se o ativo do gráfico, é o mesmo do ativo usado no replay, e se o serviço de replay, esta sendo executado. Se qualquer uma destas duas coisas, não forem satisfeitas, o indicador deverá ser removido do gráfico. Feito isto, a próxima coisa que fazemos, é capturar o valor contido da variável global de terminal, onde o que queremos, é o dados que indica qual a ID do gráfico, que o serviço de replay criou. E vamos comparar este valor, com o da janela do gráfico. Caso eles sejam diferentes, o indicador será removido do gráfico. Depois disto, pegamos o valor capturado, e deslocamos ele 63 bits para a direita. Verificamos se este bit esta ou não ligado. Caso positivo, o indicador será removido do gráfico.

Apesar de tudo isto, parecer ser o suficiente, ainda não é o bastante, temos ainda um problema. Este de certa forma, me deu um certo trabalho para resolver, de maneira a manter as coisas dentro do MQL5. Digo isto, pois foi necessário adicionar uma linha ao código. Pois sem ela, toda a vez que fosse tentado travar o sistema, para que um indicador não fosse colocado no gráfico, ele era colocado. Apesar de não atrapalhar, ele ficava ali na janela de indicadores. Atormentando e me dando nos nervos. Até que tive a ideia de trocar o nome do indicador. Mas de uma forma que apenas e somente, o primeiro indicador, irá receber este novo nome. Este indicador é criado juntamente com o gráfico, no momento em que o serviço cria o mesmo.

Assim que o template é aplicado no gráfico, o indicador vem junto. Em falando no template, tem mais uma coisa sobre ele. Mas primeiro, vamos terminar esta explicação aqui. Finalmente depois de todos estes passos, fazemos uma operação OR e armazenamos o valor na variável global de terminal, de forma a travar o indicador de controle. Para terminar este tópico, temos mais uma coisa que deve ser feita. Nada disto que foi feito aqui, teria nenhuma serventia, e não funciona de forma correta, se você não fizer uma ultima mudança. Esta eu poderia deixar passar, ficando calado e me vangloriando, por ter feito algo, que muitos dizem ser impossível. Se alguém tentasse fazer, não irá conseguir, e não estou querendo dizer, que sou o cara. Que ninguém consegue fazer o que faço. Não preciso e nem quero tais títulos. O que quero, é mostrar, que dá para fazer muito mais, do que muitos acham ser possível, e as vezes, a solução de um problema, passa por lugares, que nem se quer imaginamos.

Se você pegar toda esta explicação até aqui, e tentar executar a coisa toda. Irá notar que terá sucesso em todos os pontos. Menos em um. Sempre que você tentar, não irá conseguir impedir, que novos indicadores de controle sejam adicionados no gráfico, que foi criado pelo serviço de replay. Não adianta tentar, você não conseguirá impedir de novos indicadores de controle, sejam adicionados. Mas como assim ?!?! Você até adicionou uma linha para fazer isto ?!?! Como isto é possível ?!?! Você esta de brincadeira não é mesmo ?!?!

Bem que eu queria estar brincando, mas não, isto é serio. Você não vai conseguir impedir, de isto acontecer. Isto por que, existe uma " falha ", notem as aspas. Em todo o sistema. Não sei bem o por que, ou como é possível, tal coisa acontecer. Mas vejam o código abaixo:

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;
}

No exato momento, em que esta linha é executada, acontece algo estranho. Quebrei a cabeça por um bom tempo, até que tive a curiosidade de abrir o template, e ver como ele funciona. Até entender, por que ao forçar este sistema, a não usar um template, mas sim criando e aplicando o indicador no gráfico, de forma automática, conforme expliquei na no começo do artigo. Onde mostrei que o simples fato, de você usar um serviço, não nos permite adicionar indicadores no gráfico, mas ao usar um script, com o mesmo código, usado no serviço, era sim possível fazer a adição de indicadores. O sistema funcionava, mas quando usava o template, não funcionava.

Procurei e garimpei bastante. Não encontrei ninguém que tinha a resposta. Até que usando artifícios e técnicas adquiridos, por conta de anos de programação, encontrei o problema. Se você observar nos artigos anteriores, os dados presentes no arquivo de template, irá encontrar os seguintes dados:

<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

Você pode olhar e achar que não existe nada de errado aqui, e de fato não existe. Mas o simples fato, de mudar algo contido neste fragmento acima. Irá fazer o sistema funcionar, da forma como é esperado. Ou seja, ele irá finalmente conseguir travar o indicador de controle. Impedindo que outros sejam adicionados.

A versão corrigida se encontra logo abaixo:

<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

Não vou mostrar exatamente o ponto mudado, quero que você veja e tente entender. Mas não se preocupe, na versão em anexo do código, o sistema já estará funcionado.


Conclusão

Mas como foi dito: Eu poderia deixar isto passar, e quando alguém viesse perguntar, por que não estava conseguindo fazer o sistema travar. Eu poderia fazer de conta sou o maior, e melhor programador que existe, ou já existiu. Mas não gosto de ficar me vangloriando. Quero que mais gente aprenda, entende e se sinta motivado a procurar sempre uma solução. E sempre que possível, divulgue as coisas, pois é assim que fazemos as coisas evoluírem. Guardar o conhecimento, não é sinal de grandeza, é sinal de medo ou falta de confiança. A ponto de não aceitar criticas, ou aceitar que outros podem aprender com você, e se tornarem melhores que você.

Já superei esta fase. Por conta disto, estou mostrado como fazer as coisas. Espero que muitos também se sintam motivados a fazer o mesmo. Um forte abraço a todos, e nos vemos no próximo artigo. O nosso trabalho está apenas começando.




Arquivos anexados |
Market_Replay.zip (13058.5 KB)
Algoritmos de otimização populacionais: algoritmo de otimização de forrageamento bacteriano (BFO) Algoritmos de otimização populacionais: algoritmo de otimização de forrageamento bacteriano (BFO)
A base da estratégia de forrageamento de E. coli (E. coli) inspirou cientistas a desenvolverem o algoritmo de otimização BFO. Esse algoritmo apresenta ideias originais e abordagens promissoras para otimização e merece um estudo mais aprofundado.
Experiências com redes neurais (Parte 3): Uso pratico Experiências com redes neurais (Parte 3): Uso pratico
As redes neurais são tudo para nós. E vamos verificar na prática se é assim, indagando se MetaTrader 5 é uma ferramenta autossuficiente para implementar redes neurais na negociação. A explicação vai ser simples.
Algoritmos de otimização populacionais: Otimização de ervas invasivas (IWO) Algoritmos de otimização populacionais: Otimização de ervas invasivas (IWO)
A surpreendente capacidade das plantas daninhas de sobreviver em uma ampla variedade de condições foi a inspiração para o desenvolvimento de um poderoso algoritmo de otimização. O IWO (Invasive Weed Optimization) é considerado um dos melhores entre os analisados até o momento.
Algoritmos de otimização populacionais: Algoritmo do morcego Algoritmos de otimização populacionais: Algoritmo do morcego
Hoje estudaremos o algoritmo do morcego (Bat algorithm, BA), que possui convergência incrível em funções suaves.