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

Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 16): Um novo sistema de classes

MetaTrader 5Testador | 21 junho 2023, 15:05
464 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior, Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 15): Nascimento do SIMULADOR (V) - RANDOM WALK, desenvolvemos uma forma de promover a randomização dos dados. Isto foi feito, de maneira que o resultado ficasse bastante adequado. Mas apesar do sistema ser de fato capaz, de já conseguir simular os tickets, de maneira plausível. Ele ainda carece de algumas informações, e não somente o simulador, mas também o próprio sistema de replay. A verdade, é que algumas das coisas que precisamos implementar, são umas das mais complicadas de serem feitas. E isto da forma como o sistema esta sendo construído. Precisamos mudar algumas coisas, para que possamos pelo menos ter uma melhor modelagem, a ponto de não nos perdermos, nas próximas etapas.

Acreditem, se você achou o RANDOM WALK complicado. É por que ainda não viu, ou não tem noção, do que será preciso desenvolver. Isto para que a experiência de simulação / replay, seja a mais adequada. Uma das questões que mais pega, é o fato de que você deve saber, pelo menos o básico sobre o ativo a ser simulado, ou do qual irá ser feito o replay. No atual momento de desenvolvimento, não estou me preocupando, em resolver algumas questões de forma automática. Não que isto seja difícil de ser feito. Mas sim, por que existem formas mais práticas de faze-lo.

Se você observou bem, o artigo anterior, deve ter notado, que é possível você utilizar programas como EXCEL, para poder gerar possíveis cenários de mercado. Mas pode ser, que você também utilize informações de outros ativos, e os misture de forma, a gerar um tipo de simulação mais complexa. O fato de fazer, ou poder fazer isto, dificulta a confecção de um sistema totalmente automático. Que seria capaz, de ir e buscar as informações faltantes, do próprio ativo. Mas também temos uma outra questão. Esta é de menor ordem. Por conta disto, aqui irei recortar e distribuir o conteúdo do arquivo de cabeçalho C_Replay.mqh, de tal maneira, que a manutenção e melhorias, seja feitas de forma mais simples. Pois trabalhar com uma classe muito grande, além de ser inconveniente, é algo que não me agrada. O motivo, é que muitas das vezes, o código pode estar pouco otimizado. Assim, para facilitar as coisas, iremos fazer ambas mudanças. Distribuir o conteúdo a classe C_Replay em mais classes, e ao mesmo tempo, iremos implementar, as partes mais urgentes.


Implementando o serviço com um novo sistema de classes

Apesar do código visto, em artigos anteriores, ter sofrido muita pouca mudança de uma forma geral. Esta nova implementação, traz consigo um novo modelo. Este é ao mesmo tempo mais simples de expandir, com também de ser compreendido por aqueles que não tem um grande conhecimento em programação. Isto por que as coisas são ligadas, de uma forma mais simples. Não teremos rotinas longas e cansativas de serem lidas. Existem alguns detalhes, que para alguns pode parecer um pouco estranho. Mas por mais bizarro que venha a se apresentar, tornam o código, além de mais seguro e estável, mais sustentável em termos de estruturação. Um destes detalhe, é o uso de ponteiros, não da mesma forma que os usamos na linguagem C++ ou C legado. Mas tendo um comportamento muito parecido com tais linguagens.

O uso de ponteiros, nos permite fazer coisas, que de outra forma não é possível. Apesar de que muitas das possibilidades presentes no C++, não serem possíveis em MQL5. O simples fato de podermos usar os ponteiros, na sua forma mais simples, já nos permite modelar as coisas, de uma maneira bem mais maleável e agradável. Pelo menos no que tange a parte da programação e sintaxe.

Por conta disto, o novo arquivo do serviço, na atual versão, que será encontrado no anexo, passou a ser como mostrado abaixo:

#property service
#property icon "\\Images\\Market Replay\\Icon.ico"
#property copyright "Daniel Jose"
#property version   "1.16"
#property description "Serviço de Replay-Simulador para plataforma MT5."
#property description "Este é dependente do indicador Market Replay."
#property description "Para mais detalhes sobre esta versão veja o artigo:"
#property link "https://www.mql5.com/pt/articles/11095"
//+------------------------------------------------------------------+
#define def_Dependence  "\\Indicators\\Market Replay.ex5"
#resource def_Dependence
//+------------------------------------------------------------------+
#include <Market Replay\C_Replay.mqh>
//+------------------------------------------------------------------+
input string            user00 = "Config.txt";  //Arquivo de configuração do Replay.
input ENUM_TIMEFRAMES   user01 = PERIOD_M1;     //Tempo gráfico inicial.
input bool              user02 = true;          //Visualizar a construção das barras.
input bool              user03 = true;          //Visualizar métricas de criação.
//+------------------------------------------------------------------+
void OnStart()
{
        C_Replay        *pReplay;

        pReplay = new C_Replay(user00);
        if (pReplay.ViewReplay(user01))
        {
                Print("Permissão concedida. Serviço de replay já pode ser utilizado...");
                while (pReplay.LoopEventOnTime(user02, user03));
        }
        delete pReplay;
}
//+------------------------------------------------------------------+

Parece que ele não sofreu grandes mudanças, mas sim, ele esta bem diferente. Apesar de que para o usuário final, ele irá continuar sendo o mesmo, e tendo um comportamento idêntico ao encontrado, nas versões anteriores. Mas para a plataforma e principalmente, para o sistema operacional. O arquivo executável gerado, terá um comportamento e será visto de uma forma diferente. O motivo é que estamos utilizando a classe, não mais como uma variável, e sim como um ponteiro de memória. E é justamente por conta disto, que todo o sistema passa a ter um comportamento bem diferente. Se a programação for feita, com bastante selo e cuidado. As classes serão ainda mais seguras e estáveis, do que as classes usadas apenas e somente como variáveis. Apesar de que, elas tenham o mesmo comportamento, ela será diferente no uso da memória.

Agora pelo fato de estarmos utilizando as classes como ponteiros, iremos abrir mão de algumas coisas, e passaremos a utilizar outras. A primeira delas, é o fato de que agora a classe sempre será inicializada, e sempre será encerrada. Não mais de forma implícita, mas sim de forma explicita. Isto é feito utilizando os operadores new e delete. Quando fazemos o uso do operador new, para criar uma classe, deveremos sempre chamar um constructor de classe. Estes nunca retornam um valor, desta forma não há como testar um retorno diretamente. Precisamos fazer isto, em outro momento. A mesma coisa acontece, ao utilizar o operador delete, este irá chamar o destructor da classe.  Igual o constructor da classe, o destructor nunca retorna um valor. Mas diferente do constructor, o destructor não recebe nenhum tipo de argumento.

Você sempre terá que fazer desta forma: Criar a classe usando o operador new, e destruir a classe usando o operador delete. Este será o único trabalho, que de fato você terá que fazer. Todo o restante é feito pelo sistema operacional, que irá alocar memória suficiente, para que o programa rode em uma região da memória. O que o torna mais seguro possível durante todo o seu tempo de vida. Mas aqui mora um perigo, para quem esta acostumado a utilizar ponteiros no C++/C, que é a notação. Na linguagem C++ e C, toda a vez que vamos nos referir a ponteiros, temos uma notação bem especifica. Normalmente utilizamos um sinal de menos seguido de um sinal de maior ( -> ). Para um programador C++/C, isto quer dizer que estamos utilizando um ponteiro. Mas também podemos usar uma notação diferente, que será vista nos meus códigos, quando este estiver se referindo a um ponteiro.

Além é claro de que o nome da variável, que normalmente começar com a letra ( p ) ou ser usada uma combinação do tipo ( ptr ), ( apesar de tudo esta não é uma regra rígida, então não se apeguem a ela ) . Apesar de o MQL5 aceitar tanto a notação que é vista no código acima, quanto a que será vista logo abaixo. Pessoalmente acho mais simples de ler um código que utiliza ponteiros, quando este de fato usa a declaração adequada. Por conta disto nos meus códigos, a notação será a mostrada abaixo. Isto por conta de costume com a linguagem C++/C:

void OnStart()
{
        C_Replay        *pReplay;

        pReplay = new C_Replay(user00);
        if ((*pReplay).ViewReplay(user01))
        {
                Print("Permissão concedida. Serviço de replay já pode ser utilizado...");
                while ((*pReplay).LoopEventOnTime(user02, user03));
        }
        delete pReplay;
}

Existe de fato um trabalho extra, na parte envolvendo a digitação do código. Mas para mim, que programo a anos em C++/C, é mais simples entender que estou me referindo a um ponteiro, ao ver um código como o mostrado acima. E já que o MQL5 entende a coisa, da mesma forma que o C++/C entenderia. Não vejo problemas em utilizar esta notação. Toda a vez que você, vier a ver um código, com uma notação igual a mostrada acima, não se preocupem, tratasse apenas de um ponteiro.

Dito isto, podemos continuar a explorar o novo sistema de classes. Se você acha que a coisa mudou apenas neste ponto, você esta bem otimista. O simples fato de já ter feito estas mudanças, onde iremos garantir, de forma explicita, que uma classe será construída e destruída, em momentos bem específicos. Já irá nos força, a fazer diversas outras mudanças no código. Um constructor e um destructor, não retornam nenhum valor. Precisamos então, ter que fazer alguma coisa, de maneira a saber, se a classe foi corretamente construída ou não.

Para entender como fazer isto, vamos olhar dentro da caixa preta da classe C_Replay. Esta se encontra no arquivo de cabeçalho C_Replay.mqh. A estrutura interna é vista na imagem abaixo:

Figura 01 - C_Replay.mqh

Figura 01 - Sistema de ligação das classe de Replay


A figura 01, mostra como as classes estão ligadas entre si, de forma a termos o funcionamento desejado e previsto pelo serviço de replay / simulação. A seta verde, indica que a classe é importada, de forma que algumas das coisas interna da classe, serão publicas. Já a seta vermelha indica, que os dados serão importados, mas que eles já não serão vistos, em classes acima. Já a classe C_FilesBars, é uma classe solta. Ela na verdade, não será herdada por nenhuma outra, mas terá seus métodos utilizados pelas demais classes.

Para de fato entender, como estas ligações se dão. Será preciso ver, o que esta acontecendo por debaixo dos panos. Para fazer isto, teremos que ver como cada uma das classes, foi criada. E como elas estão dentro de seus respectivos arquivos. Estes arquivos sempre recebem o mesmo nome da classe. Isto não é obrigatório, mas é de boa pratica. Já que o código passou por algumas mudanças, e muitas delas apenas para deixar a coisa mais agradável, e organizada. Não irei entrar, de fato, em muitos detalhes sobre ele. Mas em alguns momentos, explicarei as mudanças que acontecerem e o motivo. Pode ser algo bastante valido para quem esta começando a aprender a programar. Já que é bem mais simples aprender, quando temos um código funcional, e vamos modificando o mesmo, de forma a gerar algo diferente. Mas ao mesmo tempo algo que queremos, mas mantendo sempre o código funcionando.

Então vamos começar, a entender como a construção foi feita.


Uma classe solta : C_FileBars

Esta classe é bastante simples, e conta com tudo que precisamos, para poder fazer a leitura das barras, presentes em um arquivo. Ela não é uma classe muito grande. Desta forma, todo o código da mesma, pode ser visto na integra logo abaixo:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "Interprocess.mqh"
//+------------------------------------------------------------------+
#define def_BarsDiary   1440
//+------------------------------------------------------------------+
class C_FileBars
{
        private :
                int     m_file;
                string  m_szFileName;
//+------------------------------------------------------------------+
inline void CheckFileIsBar(void)
                        {
                                string  szInfo = "";
                                
                                for (int c0 = 0; (c0 < 9) && (!FileIsEnding(m_file)); c0++) szInfo += FileReadString(m_file);
                                if (szInfo != "<DATE><TIME><OPEN><HIGH><LOW><CLOSE><TICKVOL><VOL><SPREAD>")
                                {
                                        Print("Arquivo ", m_szFileName, ".csv não é um arquivo de barras.");
                                        FileClose(m_file);
                                        m_file = INVALID_HANDLE;
                                }
                        }
//+------------------------------------------------------------------+
        public  :
//+------------------------------------------------------------------+
                C_FileBars(const string szFileNameCSV)
                        :m_szFileName(szFileNameCSV)
                        {
                                if ((m_file = FileOpen("Market Replay\\Bars\\" + m_szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) == INVALID_HANDLE)
                                        Print("Falha ao acessar ", m_szFileName, ".csv de barras.");
                                else
                                        CheckFileIsBar();
                        }
//+------------------------------------------------------------------+
                ~C_FileBars()
                        {
                                if (m_file != INVALID_HANDLE) FileClose(m_file);
                        }
//+------------------------------------------------------------------+
                bool ReadBar(MqlRates &rate[])
                        {
                                if (m_file == INVALID_HANDLE) return false;
                                if (FileIsEnding(m_file)) return false;
                                rate[0].time = StringToTime(FileReadString(m_file) + " " + FileReadString(m_file));
                                rate[0].open = StringToDouble(FileReadString(m_file));
                                rate[0].high = StringToDouble(FileReadString(m_file));
                                rate[0].low = StringToDouble(FileReadString(m_file));
                                rate[0].close = StringToDouble(FileReadString(m_file));
                                rate[0].tick_volume = StringToInteger(FileReadString(m_file));
                                rate[0].real_volume = StringToInteger(FileReadString(m_file));
                                rate[0].spread = (int) StringToInteger(FileReadString(m_file));
                                
                                return true;
                        }
//+------------------------------------------------------------------+
                datetime LoadPreView(const string szFileNameCSV)
                        {
                                int      iAdjust = 0;
                                datetime dt = 0;
                                MqlRates Rate[1];
                                
                                Print("Carregando barras previas para Replay. Aguarde ....");
                                while (ReadBar(Rate) && (!_StopFlag))
                                {
                                        iAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);
                                        dt = (dt == 0 ? Rate[0].time : dt);
                                        CustomRatesUpdate(def_SymbolReplay, Rate, 1);
                                }
                                return ((_StopFlag) || (m_file == INVALID_HANDLE) ? 0 : Rate[0].time + iAdjust);
                        }
//+------------------------------------------------------------------+
};

Você pode estar pensando que esta classe, não faz muito sentido. Mas ela contém tudo, que é preciso, para podermos abrir, ler, montar o ativo customizado, e fechar o arquivo de barras. Tudo isto é feito em etapas, bem especificas, de forma que não importa o que mudemos aqui dentro. Qualquer um que queira ler um arquivo de barras, conseguirá fazer isto, bem aqui.

Esta classe foi desenhada e desenvolvida, de forma que ela deverá sempre utilizar os operadores NEW e DELETE. Assim, ela irá permanecer na memória, apenas pelo tempo suficiente, para que seu trabalho seja realizado. Você deve evitar ao máximo, usar esta classe, sem que seja por meio dos operadores acima indicados. Caso contrário, poderá ter problemas de estabilidade. Não que isto de fato irá acontecer. Mas o fato de ela ter sido desenhada para usar os operadores, não a torna adequada de ser usada por outro meios.

Vamos entender o seguinte: Nesta classe, temos 2 variáveis globais e privativas dela. Elas são inicializadas exatamente no constructor da classe. Onde iremos tentar abrir e testar, se o arquivo indicado, é ou não, um arquivos de barras. Mas lembre-se do seguinte fato: Um constructor, não retorna nenhum tipo de valor. Não temos como retornar absolutamente nada aqui. Mas podemos indicar, que o arquivo não corresponde ao esperado, marcando ele, como sendo um arquivo invalido. Isto claro, depois de termos fechado o mesmo. Desta forma, qualquer tentativa de leitura, irá resultar em um erro, que será reportado de forma adequada. Assim poderá ser tratada pelo chamador. Assim, a classe irá funcionar, como se fosse na verdade, um arquivo que já contem as barras. Ou um grande objeto com o conteúdo do arquivo, já presente dentro dela. Tudo que os chamadores estarão fazendo, será e deverá ser visto, como uma leitura do conteúdo deste grande objeto. Mas assim que o destructor for chamado. Teremos o fechamento do arquivo. E a classe terá sido destruída pelo processo chamador.

Pode não parecer assim tão mais seguro ou estável, este tipo de modelagem. Mas acreditem, ela é consideravelmente, muito mais segura e estável do que parece. Se bem que se fosse possível, ter acesso a alguns outros operadores, presentes no C++, a coisa iria ficar ainda mais interessante. Isto se a programação for bem feita, caso contrário seria um desastre total. Mas como o MQL5, não é o C++. Tudo bem, vamos então explorar e tirar o máximo, que esta linguagem nos permite tirar. Assim teremos um sistema, que irá estar utilizando algo, bem próximo dos limites que a linguagem nos permite fazer.


Uma classe profunda : C_FileTicks

A próxima a ser vista, é a classe C_FileTicks. Esta classe é bastante mais complexa que a classe C_FileBars. Isto devido ao fato de temos coisas que são publicas. Coisas que são privativas. E coisas que ficam no meio termo. E que recebem uma denominação especial: PROTECTED. Este termo protected, ou protegido. É algo que tem um nível especial, quando se trata de herança entre classes. Em se tratando de C++, a coisa é bem complicada. Pelo menos no começo do aprendizado. Isto por conta de alguns operadores presentes no C++. Mas felizmente o MQL5, trata as coisas de uma forma bem mais simples. Assim, será bem mais simples de entender, como as coisas declaradas como sendo protegidas, são herdadas e podem ser acessadas ou não. Dependendo é claro, da forma como a herança aconteça. Para isto, veja a tabela logo abaixo:

Definição na classe Base Tipo de herança da classe Base Acesso dentro da classe Derivada Acesso via chamada a classe derivada 
private public Acesso negado Não é possível acessar dados ou procedimentos na classe base
public public Acesso permitido Acesso permitido aos dados ou procedimentos na classe base
protected public Acesso permitido Não é possível acessar dados ou procedimentos na classe base
private private Acesso negado Não é possível acessar dados ou procedimentos na classe base
public private Acesso permitido Não é possível acessar dados ou procedimentos na classe base
protected private Acesso permitido Não é possível acessar dados ou procedimentos na classe base
private protected Acesso negado Não é possível acessar dados ou procedimentos na classe base
public protected Acesso permitido  Não é possível acessar dados ou procedimentos na classe base
protected protected Acesso permitido Não é possível acessar dados ou procedimentos na classe base

Tabela de nível de acesso a elementos e procedimentos das classe

Notem que, em apenas um único caso, de fato podemos acessar os dados, ou procedimentos, dentro de uma classe, ao usar o sistema de herança, ou as definições de acesso. E é justamente quando tudo, é declarado como sendo publico. Já em todos os outros casos, a coisa é mais ou menos possível, no nível de herança da classe. Mas não é possível, nenhum acesso a procedimentos ou dados, que estão dentro da classe base. Isto independente da clausula de acesso.

Agora um detalhe: Se você declarar algo, como sendo protected, e tentar acessar diretamente este dado, ou procedimento, sem que seja feita via herança de classe. Você não conseguirá acessar tal dado, ou procedimento. Isto por que, sem o uso da herança, estes dados ou procedimentos, declarados como sendo protegidos. Serão tratados ,como sendo privativos. Assim o acesso a eles, será negado.

Isto parece ser bem complicado, não é mesmo ?!?! Mas não precisa ficar desesperado, arrancando os cabelos. Na pratica a coisa é bem mais simples. Se bem, que você terá que experimentar algumas vezes, este mecanismo em funcionamento, para de fato compreender seu funcionamento. Mas acreditem, isto aqui no MQL5, é muito, mas muito mais simples, e sem sombra de dúvida, do que no C++. Lá a coisa é extremamente mais complicada. E o motivo é que temos formas, de mudar o nível de acesso a dados ou procedimentos, que estão declarados como sendo protegidos. Em alguns casos, até mesmo os privativos, durante o processo de herança da classe. Lá a é coisa de doido mesmo. Mas aqui no MQL5 é suave.

Com base nisto, podemos finalmente ver a classe C_FileTicks. Que apesar de ser mais complicada em tese. Tem um código relativamente simples. Vamos então começar, vendo as primeiras coisas dentro da classe, a partir de sua declaração:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_FileBars.mqh"
//+------------------------------------------------------------------+
#define def_MaxSizeArray        16777216 // 16 Mbytes de posições
//+------------------------------------------------------------------+
#define macroRemoveSec(A) (A - (A % 60))
//+------------------------------------------------------------------+
class C_FileTicks
{
        protected:
                struct st00
                {
                        MqlTick  Info[];
                        MqlRates Rate[];
                        int      nTicks,
                                 nRate;
                }m_Ticks;
                double          m_PointsPerTick;

Vejam que é algo simples, mas tomem cuidado. Dado o fato de que esta estrutura, e esta variável, pode ser acessada, seguindo a tabela de nível de acesso.  Feito isto, temos uma serie de procedimentos privativos da classe. Estes não podem ser acessados fora da classe. Servindo para dar apoio, aos procedimentos que são públicos, que são dois, e podem ser vistos abaixo:

        public  :
//+------------------------------------------------------------------+
                bool BarsToTicks(const string szFileNameCSV)
                        {
                                C_FileBars      *pFileBars;
                                int             iMem = m_Ticks.nTicks;
                                MqlRates        rate[1];
                                MqlTick         local[];
                                
                                pFileBars = new C_FileBars(szFileNameCSV);
                                ArrayResize(local, def_MaxSizeArray);
                                Print("Convertendo barras em ticks. Aguarde...");
                                while ((*pFileBars).ReadBar(rate) && (!_StopFlag)) Simulation(rate[0], local);
                                ArrayFree(local);
                                delete pFileBars;
                                
                                return ((!_StopFlag) && (iMem != m_Ticks.nTicks));
                        }
//+------------------------------------------------------------------+
                datetime LoadTicks(const string szFileNameCSV, const bool ToReplay = true)
                        {
                                int             MemNRates,
                                                MemNTicks;
                                datetime dtRet = TimeCurrent();
                                MqlRates RatesLocal[];
                                
                                MemNRates = (m_Ticks.nRate < 0 ? 0 : m_Ticks.nRate);
                                MemNTicks = m_Ticks.nTicks;
                                if (!Open(szFileNameCSV)) return 0;
                                if (!ReadAllsTicks()) return 0;
                                if (!ToReplay)
                                {
                                        ArrayResize(RatesLocal, (m_Ticks.nRate - MemNRates));
                                        ArrayCopy(RatesLocal, m_Ticks.Rate, 0, 0);
                                        CustomRatesUpdate(def_SymbolReplay, RatesLocal, (m_Ticks.nRate - MemNRates));
                                        dtRet = m_Ticks.Rate[m_Ticks.nRate].time;
                                        m_Ticks.nRate = (MemNRates == 0 ? -1 : MemNRates);
                                        m_Ticks.nTicks = MemNTicks;
                                        ArrayFree(RatesLocal);
                                }
                                return dtRet;
                        };
//+------------------------------------------------------------------+

Todos os procedimentos privativos, são procedimentos, que já foram observados e analisados em artigos anteriores nesta serie. De fato não irei entrar em detalhes sobre os mesmos aqui, já que eles não sofreram nenhuma, ou quase nenhuma mudança, com relação ao que foi explicado. No entanto, com relação a estes 2 procedimentos públicos, tem algo que merece uma atenção mais especial. O detalhe todo esta na rotina BarsToTicks. Então atenção ao seguintes pontos: No tópico anterior, falei que a classe C_FileBars, é uma classe solta. Que não é de fato herdada por ninguém. Isto é fato, até o presente momento. Mas, também mencionei, que ela seria usada em pontos específicos. No momento que isto ocorresse, ela deveria ser acessada de uma forma bastante especifica.

Pois bem, eis aqui um destes pontos. Primeiramente declaramos a classe, de uma forma bem especifica. Agora chamamos o constructor da classe, com o nome do arquivo, do qual queremos obter os valores das barras. Lembre-se: Esta chamada não irá retornar nenhum valor. O que estamos fazendo, é usando o operador NEW, para que a classe venha a receber, um espaço na memória reservado, apenas e somente para ela. É neste espaço que ela irá ficar contida, o MetaTrader 5, não tem de fato, controle de onde a classe pode estar. Apenas o sistema operacional, tem esta informação.

Mas convenientemente, recebemos um valor do operador NEW, e este valor é um "ponteiro", que podemos usar para referenciar diretamente a nossa classe ( NOTA : A palavra ponteiro, esta entre aspas, por não ser de fato um ponteiro. Trata-se apenas de uma variável, que é capaz de referenciar uma classe. Como se esta classe fosse uma outra variável, ou constante. Em outro momento no futuro, irei mostrar como você pode usar este artificio, para produzir uma classe constante. Na qual podemos, apenas acessar dados, mas não efetuar computações. Já que é algo com uma aplicação bastante especifica, irá ficar para uma outra oportunidade ). Uma vez que temos, o tal "ponteiro", podemos efetuar o trabalho em nossa classe. Mas novamente, não temos ainda, a certeza de que o arquivo, se encontra aberto, podendo ser lido. Por conta disto, teríamos que efetuar algum tipo de teste, antes de uma tentativa de leitura, e utilização de qualquer dado. Felizmente isto não é necessário, já que durante uma tentativa de leitura, podemos testar, se a chamada foi ou não devidamente executada. Ou seja, se realmente ocorreu uma leitura de dados, ou se tal leitura não ocorreu. E isto é feito neste ponto.

Caso a leitura falhe, por qualquer motivo. O laço WHILE, simplesmente irá encerrar. Então não precisamos de fato, ficar analisando as coisas. A própria tentativa de ler algum dado, servirá com um teste, para indicar se a leitura foi ou não, bem sucedida. Assim seremos capazes de controlar as coisas, fora da classe C_FileBars. Mas precisamos encerrar, de forma explicita a classe. De maneira, que a memória na qual foi alocada, seja devolvida ao sistema operacional. Isto é feito, ao se efetuar uma chamada ao destructor, via operador DELETE. Assim, garantimos que a classe foi devidamente removida, e que não existem mais referencias a ela.

O fato de você não fazer isto, da maneira como está sendo mostrado. Pode gerar dados inconsistentes, e até mesmo o uso de "lixo", em seus programas. Mas seguindo o procedimento descrito acima, você saberá exatamente quando, onde e como uma classe, estará sendo usada. O que pode lhe ajudar, em vários cenários, onde a modelagem poderá ser bastante complexa.


Uma classe, várias funções : C_ConfigService

Esta classe é bastante curiosa. Pois apesar de ser uma classe, que faz um tipo de elo, entre a classe C_FileTicks, e a classe C_Replay. Ela de certa forma, assegura que as coisas irão se manter, dentro do esperado. E que as melhorias, ou mudanças no sistema de configuração, irão refletir, apenas nos locais, onde definitivamente, precisam ser de fato vistos. Não se trata de uma classe muito extensa, ou mesmo complicada. Ela é apenas uma classe intermediária, tendo um código bastante simples, diga-se por sinal. A ideia, é colocar nela, toda e qualquer coisa, que se relacione ao trabalho, de configura o serviço de replay / simulação. Este trabalho basicamente, é ler o arquivo de configuração, e aplicar o que esta nele, ao serviço, de modo que este funcione conforme configurado pelo usuário.

O trabalho desta classe se resume em ficar: Lendo e criando tickets a serem simulados. Aplicando as barras ao ativo de replay. E em alguns casos, ajustando as variáveis do ativo de replay. Assim o ativo terá um comportamento, o mais similar possível ao do ativo real. Seu código completo, e isto no atual estagio de desenvolvimento, pode ser visto logo abaixo:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_FileBars.mqh"
#include "C_FileTicks.mqh"
//+------------------------------------------------------------------+
class C_ConfigService : protected C_FileTicks
{
        protected:
//+------------------------------------------------------------------+
                datetime m_dtPrevLoading;
//+------------------------------------------------------------------+
        private :
//+------------------------------------------------------------------+
                enum eTranscriptionDefine {Transcription_INFO, Transcription_DEFINE};
//+------------------------------------------------------------------+
inline eTranscriptionDefine GetDefinition(const string &In, string &Out)
                        {
                                string szInfo;
                                
                                szInfo = In;
                                Out = "";
                                StringToUpper(szInfo);
                                StringTrimLeft(szInfo);
                                StringTrimRight(szInfo);
                                if (StringSubstr(szInfo, 0, 1) == "#") return Transcription_INFO;
                                if (StringSubstr(szInfo, 0, 1) != "[")
                                {
                                        Out = szInfo;
                                        return Transcription_INFO;
                                }
                                for (int c0 = 0; c0 < StringLen(szInfo); c0++)
                                        if (StringGetCharacter(szInfo, c0) > ' ')
                                                StringAdd(Out, StringSubstr(szInfo, c0, 1));                                    
                                
                                return Transcription_DEFINE;
                        }
//+------------------------------------------------------------------+
inline bool Configs(const string szInfo)
                        {
                                const string szList[] = {
                                        
						"POINTSPERTICK"
                                                        };
                                string  szRet[];
                                char    cWho;
                                
                                if (StringSplit(szInfo, '=', szRet) == 2)
                                {
                                        StringTrimRight(szRet[0]);
                                        StringTrimLeft(szRet[1]);
                                        for (cWho = 0; cWho < ArraySize(szList); cWho++) if (szList[cWho] == szRet[0]) break;
                                        switch (cWho)
                                        {
                                                case 0:
                                                        m_PointsPerTick = StringToDouble(szRet[1]);
                                                        return true;                                            
                                        }
                                        Print("Variável >>", szRet[0], "<< não definida.");
                                }else
                                        Print("Definição de configuração >>", szInfo, "<< invalida.");
                                        
                                return false;
                        }
//+------------------------------------------------------------------+
inline void FirstBarNULL(void)
                        {
                                MqlRates rate[1];
                                
                                rate[0].close = rate[0].open =  rate[0].high = rate[0].low = m_Ticks.Info[0].last;
                                rate[0].tick_volume = 0;
                                rate[0].real_volume = 0;
                                rate[0].time = m_Ticks.Info[0].time - 60;
                                CustomRatesUpdate(def_SymbolReplay, rate, 1);
                        }
//+------------------------------------------------------------------+
inline bool WhatDefine(const string szArg, char &cStage)
                        {
                                const string szList[] = {
                                        "[BARS]",
                                        "[TICKS]",
                                        "[TICKS->BARS]",
                                        "[BARS->TICKS]",
                                        "[CONFIG]"
                                                        };
                                                                                                
                                cStage = 1;
                                for (char c0 = 0; c0 < ArraySize(szList); c0++, cStage++)
                                        if (szList[c0] == szArg) return true;
                                        
                                return false;
                        }
//+------------------------------------------------------------------+
        public  :
//+------------------------------------------------------------------+
                bool SetSymbolReplay(const string szFileConfig)
                        {
                                int             file,
                                                iLine;
                                char            cError,
                                                cStage;
                                string          szInfo;
                                bool            bBarPrev;
                                C_FileBars      *pFileBars;
                                
                                if ((file = FileOpen("Market Replay\\" + szFileConfig, FILE_CSV | FILE_READ | FILE_ANSI)) == INVALID_HANDLE)
                                {
                                        MessageBox("Falha na abertura do\narquivo de configuração.", "Market Replay", MB_OK);
                                        return false;
                                }
                                Print("Carregando dados para replay. Aguarde....");
                                ArrayResize(m_Ticks.Rate, def_BarsDiary);
                                m_Ticks.nRate = -1;
                                m_Ticks.Rate[0].time = 0;
                                bBarPrev = false;

                                iLine = 1;
                                cError = cStage = 0;
                                while ((!FileIsEnding(file)) && (!_StopFlag) && (cError == 0))
                                {
                                        switch (GetDefinition(FileReadString(file), szInfo))
                                        {
                                                case Transcription_DEFINE:
                                                        cError = (WhatDefine(szInfo, cStage) ? 0 : 1);
                                                        break;
                                                case Transcription_INFO:
                                                        if (szInfo != "") switch (cStage)
                                                        {
                                                                case 0:
                                                                        cError = 2;
                                                                        break;
                                                                case 1:
                                                                        pFileBars = new C_FileBars(szInfo);
                                                                        if ((m_dtPrevLoading = (*pFileBars).LoadPreView(szInfo)) == 0) cError = 3; else bBarPrev = true;
                                                                        delete pFileBars;
                                                                        break;
                                                                case 2:
                                                                        if (LoadTicks(szInfo) == 0) cError = 4;
                                                                        break;
                                                                case 3:
                                                                        if ((m_dtPrevLoading = LoadTicks(szInfo, false)) == 0) cError = 5; else bBarPrev = true;
                                                                        break;
                                                                case 4:
                                                                        if (!BarsToTicks(szInfo)) cError = 6;
                                                                        break;
                                                                case 5:
                                                                        if (!Configs(szInfo)) cError = 7;
                                                                        break;
                                                        }
                                                        break;
                                        };
                                        iLine += (cError > 0 ? 0 : 1);
                                }
                                FileClose(file);
                                switch(cError)
                                {
                                        case 0:
                                                if (m_Ticks.nTicks <= 0)
                                                {
                                                        Print("Não existem ticks para serem usados. Serviço esta sendo encerrado...");
                                                        cError = -1;
                                                }else   if (!bBarPrev) FirstBarNULL();
                                                break;
                                        case 1  : Print("O comando na linha ", iLine, " não é reconhecido pelo sistema...");    break;
                                        case 2  : Print("O sistema não esperava o conteudo da linha ", iLine);                  break;
                                        default : Print("Existe um erro na linha ", iLine);
                                }
                                                                
                                return (cError == 0 ? !_StopFlag : false);
                        }
//+------------------------------------------------------------------+
};

Aqui e especificamente, aqui, começamos a utilizar, aquela tabela de heranças. Todos estes pontos, são herdados da classe C_FileTicks. Desta maneira, estamos de fato, estendendo as funcionalidades da própria classe C_ConfigService. Mas não estamos apenas fazendo isto, pois se você observar com calma, existe uma situação bem especifica, onde precisamos carregar dados de barras previas. Para fazer isto, precisamos utilizar a classe C_FileBars. Então utilizamos a mesma forma, que fizemos na classe C_FileTicks, quando foi necessário fazer o carregamento de dados, contidos em um arquivo de barras, para transforma-los em tickets. A mesma explicação dada lá, vale aqui também.

De certa forma, esta classe, será a responsável por fazer o trabalho de tradução, dos dados contidos no arquivo de configuração. Então tudo que precisamos fazer, é definir as coisas nos pontos adequados. De forma a fazer as coisas indicarem, ou melhor dizendo, chamarem, o estagio correto. Isto para que os valores, sejam corretamente preenchidos, ou os dados sejam corretamente carregados. Isto é feito em dois locais.

O primeiro local, é onde indicaremos em que estagio, ou melhor, qual é a chave, que estamos de fato capturando, ou o que estaremos ajustando. Apesar de não ser de todo complexo. Aqui temos apenas o seguinte: Uma lista de coisas, das quais servirão de chave, para indicar no que estaremos trabalhando, nas próximas linhas no arquivo de configuração. A única atenção, que você deve ter aqui, é o fato de que esta lista, deverá seguir uma certa ordem lógica. Caso contrário, teremos problemas durante a tradução dos valores. Para saber qual a posição, que um dado elemento, tem que estar, bastará olhar na rotina SetSymbolReplay, e observar o que cada um dos valores, fazem exatamente aqui.

O segundo local, é responsável por transcrever os valores contidos, no arquivo de configuração do replay / simulação, em constantes que serão utilizadas dentro do serviço. Aqui faremos quase a mesma coisa, que foi feito antes. Só que desta vez, cada um dos valores contidos na matriz, estará indicando o nome de uma variável dentro da classe. Então tudo que você precisa fazer, será adicionar o nome, que a variável terá no arquivo de configuração a lista. Logo depois, adicionar a posição, que ela tem na lista, a uma chamada. De forma, a modificar o valor da variável desejada. Se você não compreendeu bem o que acabei de dizer, não se preocupe. Logo irei mostrar um exemplo real, de como adicionar novas variáveis. Já que precisaremos definir algumas coisas extras aqui, neste ponto especifico.

Apesar de parecer tudo muito lindo e muito bom. Temos mais uma ultima classe para ser vista.


Classe C_Replay - Não estou entendendo nada ... Onde estão as coisas ?!?!

Esta daqui é a única classe, que de fato o serviço de replay / simulação, irá ter contato. Pensem nesta classe, como sendo uma biblioteca. Mas uma biblioteca, cuja única funcionalidade, é promover um comportamento muito similar ao que aconteceria, se ao invés de estarmos fazendo um replay ou simulação, estivéssemos de fato, tendo um contato com o mercado físico, ou a uma conta demo. Ou seja, tudo que precisaremos, e teremos que de fato fazer, é implementar as coisas dentro, e somente dentro, desta classe, para que a plataforma MetaTrader 5, consiga fazer toda a simulação do replay, parecer esta vindo de um servidor real.

Mas se você olhar o código da classe com calma, e começar a procurar as coisas. Irá ficar se perguntando: Mas onde estão as variáveis, as estruturas, e as rotinas que estão sendo chamadas ?!?! Não as estou encontrando em parte alguma !!! Tudo bem, este tipo de pensamento, de fato faz parte no inicio. Isto de deve ao fato de você, ainda não ter muita familiaridade com a herança entre as classes. Não se preocupe, estude o código com calma, e logo você começará a entender, como esta coisa de herança funciona. É bom que você comece agora, pois em breve irei mostrar algo, que é ainda mais complicado e que poderá vim a lhe confundir muito. Uma destas coisas é o POLIMORFISMO. Algo extremamente útil, mas que gera uma tremenda confusão, para quem não entendeu, se quer as questões envolvidas, no que diz respeito a forma como a herança funciona. Então estude bem este código daqui.

Bem, mas por enquanto, vamos deixar este assunto de polimorfismo para outra hora. Vamos focar no código atual, e este pode ser visto logo abaixo:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_ConfigService.mqh"
//+------------------------------------------------------------------+
class C_Replay : private C_ConfigService
{
        private :
                int             m_ReplayCount;
                long            m_IdReplay;
                struct st01
                {
                        MqlRates Rate[1];
                        bool     bNew;
                        datetime memDT;
                        int      delay;
                }m_MountBar;
//+------------------------------------------------------------------+
                void AdjustPositionToReplay(const bool bViewBuider)
                        {
                                u_Interprocess  Info;
                                MqlRates        Rate[def_BarsDiary];
                                int             iPos, nCount;
                                
                                Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
                                if (Info.s_Infos.iPosShift == (int)((m_ReplayCount * def_MaxPosSlider * 1.0) / m_Ticks.nTicks)) return;
                                iPos = (int)(m_Ticks.nTicks * ((Info.s_Infos.iPosShift * 1.0) / (def_MaxPosSlider + 1)));
                                Rate[0].time = macroRemoveSec(m_Ticks.Info[iPos].time);
                                if (iPos < m_ReplayCount)
                                {
                                        CustomRatesDelete(def_SymbolReplay, Rate[0].time, LONG_MAX);
                                        if ((m_dtPrevLoading == 0) && (iPos == 0))
                                        {
                                                m_ReplayCount = 0;
                                                Rate[m_ReplayCount].close = Rate[m_ReplayCount].open = Rate[m_ReplayCount].high = Rate[m_ReplayCount].low = m_Ticks.Info[iPos].last;
                                                Rate[m_ReplayCount].tick_volume = Rate[m_ReplayCount].real_volume = 0;
                                                CustomRatesUpdate(def_SymbolReplay, Rate, 1);
                                        }else
                                        {
                                                for(Rate[0].time -= 60; (m_ReplayCount > 0) && (Rate[0].time <= macroRemoveSec(m_Ticks.Info[m_ReplayCount].time)); m_ReplayCount--);
                                                m_ReplayCount++;
                                        }
                                }else if (iPos > m_ReplayCount)
                                {
                                        if (bViewBuider)
                                        {
                                                Info.s_Infos.isWait = true;
                                                GlobalVariableSet(def_GlobalVariableReplay, Info.u_Value.df_Value);
                                        }else
                                        {
                                                for(; Rate[0].time > m_Ticks.Info[m_ReplayCount].time; m_ReplayCount++);
                                                for (nCount = 0; m_Ticks.Rate[nCount].time < macroRemoveSec(m_Ticks.Info[iPos].time); nCount++);
                                                CustomRatesUpdate(def_SymbolReplay, m_Ticks.Rate, nCount);
                                        }
                                }
                                for (iPos = (iPos > 0 ? iPos - 1 : 0); (m_ReplayCount < iPos) && (!_StopFlag);) CreateBarInReplay();
                                Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
                                Info.s_Infos.isWait = false;
                                GlobalVariableSet(def_GlobalVariableReplay, Info.u_Value.df_Value);
                        }
//+------------------------------------------------------------------+
inline void CreateBarInReplay(const bool bViewMetrics = false)
                        {
#define def_Rate m_MountBar.Rate[0]

                                static ulong _mdt = 0;
                                int i;
                                
                                if (m_MountBar.bNew = (m_MountBar.memDT != macroRemoveSec(m_Ticks.Info[m_ReplayCount].time)))
                                {
                                        if (bViewMetrics)
                                        {
                                                _mdt = (_mdt > 0 ? GetTickCount64() - _mdt : _mdt);
                                                i = (int) (_mdt / 1000);
                                                Print(TimeToString(m_Ticks.Info[m_ReplayCount].time, TIME_SECONDS), " - Metrica: ", i / 60, ":", i % 60, ".", (_mdt % 1000));
                                                _mdt = GetTickCount64();
                                        }
                                        m_MountBar.memDT = macroRemoveSec(m_Ticks.Info[m_ReplayCount].time);
                                        def_Rate.real_volume = 0;
                                        def_Rate.tick_volume = 0;
                                }
                                def_Rate.close = m_Ticks.Info[m_ReplayCount].last;
                                def_Rate.open = (m_MountBar.bNew ? def_Rate.close : def_Rate.open);
                                def_Rate.high = (m_MountBar.bNew || (def_Rate.close > def_Rate.high) ? def_Rate.close : def_Rate.high);
                                def_Rate.low = (m_MountBar.bNew || (def_Rate.close < def_Rate.low) ? def_Rate.close : def_Rate.low);
                                def_Rate.real_volume += (long) m_Ticks.Info[m_ReplayCount].volume_real;
                                def_Rate.tick_volume += (m_Ticks.Info[m_ReplayCount].volume_real > 0 ? 1 : 0);
                                def_Rate.time = m_MountBar.memDT;
                                m_MountBar.bNew = false;
                                CustomRatesUpdate(def_SymbolReplay, m_MountBar.Rate, 1);
                                m_ReplayCount++;
                                
#undef def_Rate
                        }
//+------------------------------------------------------------------+
        public  :
//+------------------------------------------------------------------+
                C_Replay(const string szFileConfig)
                        {
                                m_ReplayCount = 0;
                                m_dtPrevLoading = 0;
                                m_Ticks.nTicks = 0;
                                m_PointsPerTick = 0;
                                Print("************** Serviço Market Replay **************");
                                srand(GetTickCount());
                                GlobalVariableDel(def_GlobalVariableReplay);
                                SymbolSelect(def_SymbolReplay, false);
                                CustomSymbolDelete(def_SymbolReplay);
                                CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay), _Symbol);
                                CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
                                CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
                                SymbolSelect(def_SymbolReplay, true);
                                m_IdReplay = (SetSymbolReplay(szFileConfig) ? 0 : -1);
                        }
//+------------------------------------------------------------------+
                ~C_Replay()
                        {
                                ArrayFree(m_Ticks.Info);
                                ArrayFree(m_Ticks.Rate);
                                m_IdReplay = ChartFirst();
                                do
                                {
                                        if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
                                                ChartClose(m_IdReplay);
                                }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
                                for (int c0 = 0; (c0 < 2) && (!SymbolSelect(def_SymbolReplay, false)); c0++);
                                CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
                                CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
                                CustomSymbolDelete(def_SymbolReplay);
                                GlobalVariableDel(def_GlobalVariableReplay);
                                GlobalVariableDel(def_GlobalVariableIdGraphics);
                                Print("Serviço de replay finalizado...");
                        }
//+------------------------------------------------------------------+
                bool ViewReplay(ENUM_TIMEFRAMES arg1)
                        {
                                u_Interprocess info;
                                
                                if (m_IdReplay == -1) return false;
                                if ((m_IdReplay = ChartFirst()) > 0) do
                                {
                                        if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
                                        {
                                                ChartClose(m_IdReplay);
                                                ChartRedraw();
                                        }
                                }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
                                Print("Aguardando permissão do indicador [Market Replay] para iniciar replay ...");
                                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);
                                while ((!GlobalVariableCheck(def_GlobalVariableReplay)) && (!_StopFlag) && (ChartSymbol(m_IdReplay) != "")) Sleep(750);
                                
                                return ((!_StopFlag) && (ChartSymbol(m_IdReplay) != ""));
                        }
//+------------------------------------------------------------------+
                bool LoopEventOnTime(const bool bViewBuider, const bool bViewMetrics)
                        {

                                u_Interprocess Info;
                                int iPos, iTest;
                                
                                iTest = 0;
                                while ((iTest == 0) && (!_StopFlag))
                                {
                                        iTest = (ChartSymbol(m_IdReplay) != "" ? iTest : -1);
                                        iTest = (GlobalVariableGet(def_GlobalVariableReplay, Info.u_Value.df_Value) ? iTest : -1);
                                        iTest = (iTest == 0 ? (Info.s_Infos.isPlay ? 1 : iTest) : iTest);
                                        if (iTest == 0) Sleep(100);
                                }
                                if ((iTest < 0) || (_StopFlag)) return false;
                                AdjustPositionToReplay(bViewBuider);
                                m_MountBar.delay = 0;
                                while ((m_ReplayCount < m_Ticks.nTicks) && (!_StopFlag))
                                {
                                        CreateBarInReplay(bViewMetrics);
                                        iPos = (int)(m_ReplayCount < m_Ticks.nTicks ? m_Ticks.Info[m_ReplayCount].time_msc - m_Ticks.Info[m_ReplayCount - 1].time_msc : 0);
                                        m_MountBar.delay += (iPos < 0 ? iPos + 1000 : iPos);
                                        if (m_MountBar.delay > 400)
                                        {
                                                if (ChartSymbol(m_IdReplay) == "") break;
                                                GlobalVariableGet(def_GlobalVariableReplay, Info.u_Value.df_Value);
                                                if (!Info.s_Infos.isPlay) return true;
                                                Info.s_Infos.iPosShift = (ushort)((m_ReplayCount * def_MaxPosSlider) / m_Ticks.nTicks);
                                                GlobalVariableSet(def_GlobalVariableReplay, Info.u_Value.df_Value);
                                                Sleep(m_MountBar.delay - 20);
                                                m_MountBar.delay = 0;
                                        }
                                }                               
                                return (m_ReplayCount == m_Ticks.nTicks);
                        }
//+------------------------------------------------------------------+
};
//+------------------------------------------------------------------+
#undef macroRemoveSec
#undef def_SymbolReplay
//+------------------------------------------------------------------+

Como você  pode notar, não é nada de outro mundo. No entanto, este código é muito mais, do que o da versão anterior. E mesmo assim, conseguimos fazer tudo que fazíamos antes. No entanto, quero destacar que todos os pontos marcados, não fazem parte realmente da classe C_Replay. Estes pontos estão sendo herdados. Isto por conta, que não quero, que os mesmos, sejam acessíveis fora da classe C_Replay. Para fazer isto, estamos herdado as coisas de forma privativa. Assim garantimos a integridade das informações herdadas. E esta classe conta com apenas, e somente duas funções, que de fato, pode ser acessadas externamente. Já que o contructor e o destructor não contam.

Mas antes de falarmos sobre o constructor e o destructor da classe. Vamos primeiro dar uma olhada, nas duas funções, que podem ser acessadas do lado de fora da classe. Em um dado momento, até cogitei deixar, apenas e somente, uma única função. Mas por motivos práticos, decidi deixar duas mesmo. Já que assim, seria mais simples fazer as coisas. A função LoopEventOnTime, já foi bastante explorada nos artigos anteriores. E já que ela aqui não sofreu nenhum tipo de modificação, não faz sentido falar, ou melhor, dar alguma explicação extra. Podemos pular ela, e focar na que sofreu mudanças: a função ViewReplay.

A função ViewReplay, sofreu apenas e somente, uma única mudança, que é justamente, este teste daqui. Este irá verificar, se o constructor da classe, conseguiu ter sucesso na inicialização da classe. Caso ele tenha falhado, a função irá retornar um valor, que deverá forçar o serviço de replay a ser finalizado. Esta é a única alteração no código, que pode ser visto com relação o encontrado nos artigos anteriores.


Considerações finais

Com todas estas mudanças, sugiro que você estude e pratique bastante o que foi visto aqui. Comparando este código que estará no anexo, com os demais códigos vistos nos artigos anteriores. No próximo artigo, iremos começar a ver uma outra questão totalmente diferente. Mas bastante curiosa, isto para dizer o mínimo sobre o assunto.

Arquivos anexados |
Ciência de Dados e Aprendizado de Máquina (Parte 13): Analisando o mercado financeiro usando a análise de componentes principais (PCA) Ciência de Dados e Aprendizado de Máquina (Parte 13): Analisando o mercado financeiro usando a análise de componentes principais (PCA)
Vamos tentar melhorar qualitativamente nossa análise dos mercados financeiros usando a análise de componentes principais (PCA). Aprenderemos como essa técnica pode ajudar a identificar padrões ocultos nos dados, identificar tendências de mercado ocultas e otimizar estratégias de investimento. Neste artigo, veremos como o PCA oferece uma nova perspectiva para a análise de dados financeiros complexos, ajudando-nos a ver informações que não percebemos usando abordagens tradicionais. Veremos se sua aplicação aos dados do mercado financeiro proporciona uma vantagem sobre a concorrência e nos ajuda a ficar um passo à frente.
Redes neurais de maneira fácil (Parte 36): Modelos relacionais de aprendizado por reforço Redes neurais de maneira fácil (Parte 36): Modelos relacionais de aprendizado por reforço
Nos modelos de aprendizado por reforço discutidos anteriormente, usamos diferentes variantes de redes convolucionais, que são capazes de identificar diferentes corpos nos dados brutos. A principal vantagem das redes convolucionais é sua capacidade de identificar objetos independentemente de sua localização. No entanto, as redes convolucionais nem sempre são capazes de lidar com as diversas deformações e ruídos que os objetos apresentam. Mas esses problemas podem ser resolvidos pelo modelo relacional.
Algoritmos de otimização populacionais: Algoritmo de mudas, semeadura e crescimento (SSG) Algoritmos de otimização populacionais: Algoritmo de mudas, semeadura e crescimento (SSG)
O algoritmo de “mudas, semeadura e crescimento” (Saplings Sowing and Growing up, SSG) é inspirado em um dos organismos mais resistentes do planeta, um exemplo notável de sobrevivência em inúmeras condições.
Experimentos com redes neurais (Parte 4): Padrões Experimentos com redes neurais (Parte 4): Padrões
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.