English Русский 中文 Español Deutsch 日本語
preview
Desenvolvendo um sistema de Replay — Simulação de mercado (Parte 03):  Ajustando as coisas (I)

Desenvolvendo um sistema de Replay — Simulação de mercado (Parte 03): Ajustando as coisas (I)

MetaTrader 5Exemplos | 21 março 2023, 13:26
483 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior, Desenvolvendo um sistema de Replay — Simulação de mercado (Parte 02): Primeiros experimentos (II), construímos um sistema que é capaz de produzir uma barra de 1 minuto, em em tempo bastante aceitável para ser usado em um replay de mercado. Você pode ter notado, que não havia nenhum tipo de controle sobre o que estava acontecendo. Tudo que podíamos fazer, era selecionar algumas coisas e ajustar outras. Uma vez que o sistema tivesse sido inicializado, tudo que poderíamos fazer se tornava bastante limitado.

Então neste artigo, vamos melhorar isto. Vamos acionar alguns controles extras, para que possamos ter um estudo mais controlado, por assim dizer. É verdade, que ainda iremos precisar fazer diversas coisas, até ter um sistema completamente funcional em termos de estudos estatísticos, isto apenas para que tenhamos o controle sobre o que irá acontecer no gráfico. Mas já será um bom começo.

Apesar de todo o trabalho que ainda iremos ter pela frente. Neste artigo, iremos apenas fazer alguns ajustes, então o mesmo será relativamente curto. Já que não irei me aprofundar em alguns detalhes neste momento. Aqui iremos apenas construir as bases dos controles necessários. Isto para que o replay se torne pelo menos, mais simples de ser feito e analisado, por aqueles que querem de fato fazer uso deste sistema.


Planejamento

Esta fase do planejamento, é algo bastante simples, já que ao observar como o sistema foi deixado funcionando no último artigo, torna evidente o que precisamos logo de inicio. Temos e precisamos criar alguma forma de controle, de maneira a poder pausar, dar play, mas principalmente, selecionar um momento especifico, onde queremos iniciar de fato o estudo.

Da forma como está, sempre iremos iniciar a partir do primeiro ticket de negociação, e caso queiramos fazer um estudo, suponhamos a partir da quinta hora de mercado, ou seja, a partir das 14 horas ( considerando que ele abra as 9:00 ), teremos que esperar 5 horas de replay rolando, para somente então podermos fazer a analise que desejamos. Isto é totalmente inviável, já que se tentarmos parar o replay, o mesmo irá ser encerrado, começando tudo de novo, desde o primeiro ticket de negociação.

Talvez agora tenha ficado claro, o que devemos realmente fazer logo de inicio, pois da forma como o esta, é completamente desmotivador usá-lo, mesmo que ele seja interessante, não tem como usar algo que funcione desta forma.

Já temos uma direção a ser tomada, podemos então passar para a fase de implementação.


Implementação

A questão da implementação, será  algo bastante interessante. Já que teremos que usar diversos caminhos. Dos mais simples, aos mais variados, para de fato conseguir implementar o sistema de controle. Mas todos são bastante simples de serem compreendidos. Desde que você preste atenção a explicação, e acompanhe os artigos, um a um, sem querer pular nenhum deles, ou queimar etapas no desenvolvimento.

Diferente do que muitos possam pensar. Não iremos utilizar DLLs no sistema. Iremos utilizar pura e simplesmente a linguagem MQL5, para implementar o sistema de replay. O curioso é que iremos utilizar a plataforma, de uma maneira bastante curiosa, e as vezes, pode parecer estranho o por que estou escolhendo este caminho. Mas a ideia, é explorar ao máximo o que o MetaTrader 5 nos fornece, mostrando até onde realmente podemos ir dentro dele para poder criar as coisa. E o fato de recorrer a uma implementação externa, tira muito da graça de se trabalhar com MQL5, dando a impressão que ela não consegue atender ao que precisamos.

Se você observar o código usado no artigo anterior, verá que lá utilizávamos um sistema fazia uso de um serviço, para de fato criar o replay, além de um script que inicializava. Ou melhor dizendo, permitia que o serviço, enviasse os ticks para dentro do símbolo customizado. Desta forma o replay de fato era criado. Basicamente, fazemos uso de um mecanismo simples de chaveamento. Mas para gerar um controle de fato, aquele método, não é adequado. Precisamos utilizar um caminho um pouco mais complicado do que aquele.


Criando um EA ultra básico.

Vamos tentar, primeiramente usar um caminho, onde o controle será feito por meio de um EA. Este irá controlar quando o serviço deverá ou não gerar os tickets para as barras.

Mas por que um EA ?!?! Bem, poderíamos utilizar no lugar do EA, um indicador que também iria funcionar da mesma forma. Mas quero usar o EA, pelo motivo de que depois iremos precisar dele, para criar a simulação de ordens. E iremos tentar, utilizar o mesmo sistema de ordens, que foi usadas em uma outra sequencia de artigos, de minha autoria, para mais detalhes veja a sequencia Desenvolvendo um EA de negociação do ZERO. Mas por enquanto, não se preocupe com o sistema de ordens, ainda temos muito trabalho até de fato chegar nele.

O código completo do nosso EA basicão, pode ser visto logo abaixo:

#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
#include <Market Replay\C_Controls.mqh>
//+------------------------------------------------------------------+
C_Controls      Control;
//+------------------------------------------------------------------+
int OnInit()
{
        Control.Init();
                
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Control.DispatchMessage(id, lparam, dparam, sparam);
}
//+------------------------------------------------------------------+

Notem que o código é o mais simples possível. Mas suficiente, para nos permitir um controle do funcionamento do serviço. Agora temos uma coisa a ser vista, que é o código em destaque acima, ou seja a classe objeto dos controles. Este código, não é muito complicado neste primeiro momento, já que iremos implementar, apenas e somente um botão, que permite dar play ou pausar o serviço de replay. Vamos então dar uma olhada nesta classe, no atual estagio de desenvolvimento.

A primeira coisa a ser observada é vista no código logo abaixo:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include <Market Replay\Interprocess.mqh>
//+------------------------------------------------------------------+
#define def_ButtonPlay  "Images\\Market Replay\\Play.bmp"
#define def_ButtonPause "Images\\Market Replay\\Pause.bmp"
#resource "\\" + def_ButtonPlay
#resource "\\" + def_ButtonPause
//+------------------------------------------------------------------+
#define def_PrefixObjectName "Market Replay _ "

Em AZUL temos um arquivo de cabeçalho bastante interessante, que depois irei mostrar com mais calma. Logo em seguida temos algumas definições, que são os objetos bitmap, que farão a vez dos botões, que representam o play e o botão de pause, nada muito complicado. Uma vez estes pontos definidos, entramos no código da classe, já que ela é bastante compacta, você pode ver todo o código na integra logo abaixo.

class C_Controls
{
        private :
//+------------------------------------------------------------------+
                string  m_szBtnPlay;
                long            m_id;
//+------------------------------------------------------------------+
                void CreateBtnPlayPause(long id)
                        {
                                m_szBtnPlay = def_PrefixObjectName + "Play";
                                ObjectCreate(id, m_szBtnPlay, OBJ_BITMAP_LABEL, 0, 0, 0);
                                ObjectSetInteger(id, m_szBtnPlay, OBJPROP_XDISTANCE, 5);
                                ObjectSetInteger(id, m_szBtnPlay, OBJPROP_YDISTANCE, 25);
                                ObjectSetInteger(id, m_szBtnPlay, OBJPROP_STATE, false);
                                ObjectSetString(id, m_szBtnPlay, OBJPROP_BMPFILE, 0, "::" + def_ButtonPause);
                                ObjectSetString(id, m_szBtnPlay, OBJPROP_BMPFILE, 1, "::" + def_ButtonPlay);
                        }
//+------------------------------------------------------------------+
        public  :
//+------------------------------------------------------------------+
                C_Controls()
                        {
                                m_szBtnPlay = NULL;
                        }
//+------------------------------------------------------------------+
                ~C_Controls()
                        {
                                ObjectDelete(ChartID(), m_szBtnPlay);
                        }               
//+------------------------------------------------------------------+
                void Init(void)
                        {
                                if (m_szBtnPlay != NULL) return;
                                CreateBtnPlayPause(m_id = ChartID());
                                GlobalVariableTemp(def_GlobalVariableReplay);
                                ChartRedraw();
                        }
//+------------------------------------------------------------------+
                void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
                        {
                                u_Interprocess Info;
                                
                                switch (id)
                                {
                                        case CHARTEVENT_OBJECT_CLICK:
                                                if (sparam == m_szBtnPlay)
                                                {
                                                        Info.s_Infos.isPlay = (bool) ObjectGetInteger(m_id, m_szBtnPlay, OBJPROP_STATE);
                                                        GlobalVariableSet(def_GlobalVariableReplay, Info.Value);
                                                }
                                                break;
                                }
                        }
//+------------------------------------------------------------------+
};

Existem duas funções principais aqui, uma é a Init e a outra é a DispatchMessage. Estas duas funções, fazem todo o trabalho que precisamos que o EA execute, neste primeiro momento. Para explicar melhor, alguns destes detalhes, vamos observar os fragmentos destas duas funções logo abaixo, começando com a função Init.

void Init(void)
{
        if (m_szBtnPlay != NULL) return;
        CreateBtnPlayPause(m_id = ChartID());
        GlobalVariableTemp(def_GlobalVariableReplay);
        ChartRedraw();
}

Quando Init é chamada, ela primeiro verifica, se os controles estão criados. Caso isto já tenha acontecido, ela irá retorna. Isto é importante, pois quando você troca o tempo gráfico, ou faz alguma mudança que obrigue a plataforma, a repor o EA novamente no gráfico, e isto é muito comum, o status do serviço de replay, não irá mudar. Isto caso o serviço esteja sendo executado, ou seja, caso ele esteja pausado ele irá continuar assim, caso esteja em play irá continuar lançando ticks.

Caso seja a primeira chamada, temos a criação dos controles básico, que por enquanto, são apenas os botões de play e pause. Logo depois, criamos uma variável global de terminal, que será usada para comunicação entre os processos do EA e do serviço. Precisamos apenas criar esta variável, não vamos indicar nenhum valor nela.

Depois disto temos que aplicar os objetos na tela. Isto é importante, pois se não for feito esta atualização forçada, o EA irá ser carregado, o serviço irá ficar parado, e você irá imaginar que o sistema travou. Quando na verdade, estaremos esperando que o MetaTrader 5, atualize o gráfico para nos, de forma que os objetos sejam plotados, e que possamos assim dar play no replay de mercado.

Notaram que é algo bastante simples. Agora vamos ver o código da rotina DispatchMessage, que no atual estagio, é igualmente simples, e pode ser vista logo abaixo:

void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        u_Interprocess Info;
                        
        switch (id)
        {
                case CHARTEVENT_OBJECT_CLICK:
                        if (sparam == m_szBtnPlay)
                        {
                                Info.s_Infos.isPlay = (bool) ObjectGetInteger(m_id, m_szBtnPlay, OBJPROP_STATE);
                                GlobalVariableSet(def_GlobalVariableReplay, Info.Value);
                        }
                        break;
        }
}

O que estamos fazendo, é usar o MetaTrader 5, para controlar as coisas para nos. Usamos uma união, u_Interprocess, para promover um ajuste, via verificação do status do botão bitmap. Desta forma, ajustamos a variável global de terminal de forma que ela passe a informação para dentro do processo de serviço, que é responsável por criar o replay.

Por conta disto, sempre iremos iniciar o sistema de replay pausado, e uma vez que o EA, com seus objetos, já esteja carregado no gráfico, podemos dar play, ou pausar o replay de mercado, quando assim desejarmos. Desta forma a coisa começa a ficar um pouco mais interessante.

Entendendo o arquivo Interprocess.mqh

Como você pode ter imaginado, o fato de termos mudado o sistema, para no lugar de usar um script, usar um EA, trouxe alguma mudanças também no serviço de replay. Antes de falarmos destas mudanças, vamos dar uma rápida passada, pelo arquivo Interprocess.mqh, que pode ser visto na integra, em seu atual estagio de desenvolvimento, no código logo abaixo:
#define def_GlobalVariableReplay "Replay Infos"
//+------------------------------------------------------------------+
union u_Interprocess
{
        double Value;
        struct st_0
        {
                bool isPlay;
                struct st_1
                {
                        char Hours;
                        char Minutes;
                }Time[3];
        }s_Infos;
};

Esta simples definição daqui, irá nos dar um nome, mas não qualquer nome, este será o nome da variável global de terminal, que será utilizada neste estagio, para permitir a comunicação entre o EA e o Serviço. Mas a parte que pode ser complicada para quem tem menos experiência em programação, será justamente a união.

Vamos entender o que esta união de fato representa, para depois entender como ela é usada, a ponto de conseguirmos passar as informações entre o EA e o Serviço. Para começar, a entender toda esta complicação, você tem que saber, quantos bits cada tipo de dado, representa ao ser usado, para facilitar a vida de você, observem a tabela abaixo:

Tipo Numero de bits
Bool 1 bit
Char ou UChar 8 bits
Short ou UShort 16 bits
Int ou UInt 32 bits
Long ou ULong 64 bits

Nesta tabela, temos os tipos inteiros com e sem sinal, seguidos pelo numero de bits, ( Não confunda Bits com Bytes). Bit é a menor unidade de informação, ela representa um estado ligado ou desligado, ou 1 e 0 no sistema binário, já o Byte representa um conjunto de bits.

Ao observar esta tabela, talvez não fique claro para alguns, a seguinte ideia : Em uma variável do tipo uchar teremos 8 variáveis do tipo bool, ou seja uma variável do tipo uchar, corresponde a uma "união" ( a palavra não seria bem esta ) de 8 variáveis do tipo bool, em um código, ficaria assim:

union u_00
{
        char info;
        bool bits[8];
}Data;

O comprimento desta união é de 8 bits, ou 1 byte. Você pode modificar o conteúdo de info, apenas escrevendo no array bits, selecionando uma posição especifica. Por exemplo: Para que Data.info seja igual a 0x12, você pode fazer uma das duas coisas mostras abaixo:

Data.info = 0x12;

ou 

Data.bits[4] = true;
Data.bits[1] = true;

De uma forma ou de outra, você terá o mesmo resultado, deste que a variável Data.info estivesse com todos os seus Bits iniciais em 0. É isto que uma união representa.

Agora voltando ao nosso código original. O maior tipo encontrado em um sistema de 64 bits, é um tipo long ( com sinal ) ou ulong ( sem sinal ), isto de sinal e sem sinal. É por conta de que no caso de se ter o sinal, você pode representar valores negativos, e quando não temos sinal, apenas valores positivos podem ser presentados. Então neste caso, teríamos algo parecido com a imagem abaixo.

Cada um dos quadrados representam 1 bit, e o nome QWORD, vem do Assembly, que é a linguagem mãe de todas a linguagens de programação modernas. Mas esta mesma estrutura, é encontrada em um outro tipo, e este tipo são os do tipo flutantes.

Variáveis flutuantes, são variáveis, cujo valor não é exato, mas ainda assim, podem ser utilizadas para representar valores computáveis. Basicamente existem 2 tipos:

Tipo Numero de bits
Float 32 bits
Double 64 bits

Da mesma forma que os tipos inteiros vistos anteriormente, onde cada bit representa um estado ligado ou desligado. Aqui nos os tipos flutuantes, não temos a mesma lógica para representar algum valor, eles seguem um principio um pouco diferente para serem criados, mas isto não vem ao caso. O detalhe aqui é justamente outro.

Ao observarmos, qual o tipo usado nas variáveis globais de terminal, vemos que elas só podem ser do tipo flutuante, e para ser mais preciso, do tipo double, ou seja 64 bits. Pergunta: Qual é o tipo inteiro, que tem o mesmo comprimento ?!?! Exatamente isto que você respondeu, o tipo Long que tem os mesmos 64 bits, ou seja, ao unirmos um tipo long e um double, poderemos representar duas coisas totalmente diferentes ao mesmo tempo.

Mas temos uma questão um pouco complicada aqui. Como você vai saber qual é o tipo usado ?!?! Pois bem, para resolver isto, não usamos um tipo completo, mas usamos os seus fragmentos, e damos nomes a estes fragmentos, desta forma, nasce a união, que é vista no código do arquivo Interprocess.mqh.

Nos de fato, não vamos usar o tipo double, não é nem um pouco adequado ou simples de se fazer, tentar escrever diretamente na mão, o valor que deverá ser criado no tipo double. Ao invés disto, usamos as partes nomeadas para fazer esta criação, e os bits adequados serão colocados com os valores corretos representando 0s ou 1s. Depois disto, colocamos o valor double na variável global de terminal, e o outro processo, que no caso será o serviço, irá pegar o valor e decodificar o mesmo, sabendo exatamente o que deve ser feito.

Vejam que isto é feito usando regras muito simples e de fácil compreensão. Já seria muito difícil de ser feito, se você tentasse usar um outro caminho, como o de criar valores flutuantes, e depois tentar entender o que este valor significa.

Desta forma, acredito que você tenha conseguido compreender o que é uma união, e como iremos usa-la de fato. Mas lembre-se: No caso de desejar usar variáveis globais de terminal, cujo tipo é double e este tem 64 bits, a união criada não poderá exceder estes mesmos 64 bits, caso contrário alguma informação irá ser perdida.


Entendendo como o serviço de replay é criado

Esta talvez é a parte com a qual você deverá prestar bastante atenção, para entender o que esta acontecendo. Se você deixar passar algo sem entender, irá se enrolar todinho. Pois apesar de parecer simples, existem detalhes que se não forem bem compreendidos, pode fazer você pirar, tentando entender porque o sistema funciona, na minha descrição e demonstrações, mas que no entanto, você não esta conseguindo fazer ele funcionar na sua estação de trabalho.

Vamos então ver o arquivo do serviço de replay. Ele atualmente ainda esta bastante compacto e singelo. Ele pode ser visto na integra no código abaixo:

#property service
#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
#include <Market Replay\C_Replay.mqh>
//+------------------------------------------------------------------+
input string    user01 = "WINZ21_202110220900_202110221759"; //Arquivo com ticks
//+------------------------------------------------------------------+
C_Replay        Replay;
//+------------------------------------------------------------------+
void OnStart()
{
        ulong t1;
        int delay = 3;
        long id;
        u_Interprocess Info;
        bool bTest = false;
        
        if (!Replay.CreateSymbolReplay(user01)) return;
        id = Replay.ViewReplay();
        Print("Aguardando permissão para iniciar replay ...");
        while (!GlobalVariableCheck(def_GlobalVariableReplay)) Sleep(750);
        Print("Serviço de replay iniciado ...");
        t1 = GetTickCount64();
        while ((ChartSymbol(id) != "") && (GlobalVariableGet(def_GlobalVariableReplay, Info.Value)))
        {
                if (!Info.s_Infos.isPlay)
                {
                        if (!bTest)     bTest = (Replay.Event_OnTime() > 0); else       t1 = GetTickCount64();
                }else if ((GetTickCount64() - t1) >= (uint)(delay))
                {
                        if ((delay = Replay.Event_OnTime()) < 0) break;
                        t1 = GetTickCount64();
                }
        }
        Replay.CloseReplay();
        Print("Serviço de replay finalizado ...");
}
//+------------------------------------------------------------------+

Se você pegar este código, e simplesmente criar o arquivo "WINZ21_202110220900_202110221759", que é usado como base na criação do replay, e tentar rodar ele. Você irá notar que nada irá acontecer, e mesmo se você utilizar o arquivo que esta no anexo, e tentar rodar deste código, também nada irá acontecer. Mas por que ?!?! O motivo é o código id = Replay.ViewReplay(); este código faz algo, que você precisa entender, para conseguir de fato usar o sistema de replay de mercado. Não importa o que você faça, se você não entender, o que esta acontecendo, nada irá fazer sentido. Mas antes de olhar o código dentro de ViewReplay(), vamos entender primeiro o fluxo de dados, do código acima.

Para entender como ele funciona na prática, vamos desmembra-lo em partes menores, começando pelo seguinte fragmento:

if (!Replay.CreateSymbolReplay(user01)) return;

Esta linha, irá carregar os dados de ticks negociados, presentes no arquivo indicado. Caso este carregamento não tenha sucesso, o serviço simplesmente irá finalizar.

id = Replay.ViewReplay();

Esta linha irá carregar o EA, mas isto será visto com mais detalhe depois. Então vamos continuar.

while (!GlobalVariableCheck(def_GlobalVariableReplay)) Sleep(750);

A linha acima, irá ficar dentro de um loop esperando que o EA seja carregado, ou que alguma outra coisa crie a variável global de terminal. Esta irá servir, como forma de comunicação entre processos que estão executando fora do ambiente do serviço.

t1 = GetTickCount64();

Já esta, executa a primeira captura para o contador interno do serviço, esta primeira captura pode ser ou não necessária. Normalmente ela é completamente desnecessária. Já que o sistema entra no modo pausa, imediatamente ao ser habilitado.

while ((ChartSymbol(id) != "") && (GlobalVariableGet(def_GlobalVariableReplay, Info.Value)))

Este ponto é bastante curioso, temos dois testes aqui. Se um deles falhar, o serviço de replay irá ser encerrado. No primeiro caso, testamos se a janela do ativo de replay, está ou não presente no terminal. Caso o operador feche esta janela, o serviço de replay será finalizado, já que o operador não estará mais executando o replay. Já no segundo caso, testamos e ao mesmo tempo que capturados, o valor presente na variável global de terminal. Caso esta variável deixe de existir, o serviço também será encerrado.

        u_Interprocess Info;

//...

        if (!Info.s_Infos.isPlay)

Aqui fazemos uma verificação da condição informada pelo operador, ou usuário do replay. Caso estejamos no modo play, este teste irá falhar. Mas caso estejamos no modo pause, ele irá ter sucesso. Notem como utilizamos a união, para capturar o bit correto dentro do valor double, sem esta união, isto seria algo impensável de ser feito.

Uma vez que estamos no modo pausado, executamos a seguinte linha:

if (!bTest) bTest = (Replay.Event_OnTime() > 0); else t1 = GetTickCount64();

Esta linha, irá permitir, que apenas e somente, o primeiro tick de negocio seja enviado para o ativo. Isto é importante por alguns motivo que será visto em outra oportunidade. Uma vez que isto tenha sido feito, em qualquer outro momento em que o serviço de replay esteja " pausado ", iremos ficar capturando o valor atual do cronometro. É verdade, que este modo pausado, não se refere ao fato do serviço estar realmente pausado, ele apenas não esta enviando tickets, para o ativo de replay, por conta disto digo que ele esta " pausado ".

Mas caso o usuário ou operador, deseje começar ou retornar a execução do replay de mercado, entramos em uma nova linha de código. Esta é vista logo abaixo:

else if ((GetTickCount64() - t1) >= (uint)(delay))

Esta linha irá testar se devemos ou não, enviar um novo ticket baseados no valor de delay, que precisamos entre um ticket e outro. Este valor é conseguido na próxima linha do código.

if ((delay = Replay.Event_OnTime()) < 0) break;

Notem que se o delay, for menor que 0, o serviço de replay irá ser encerrado. Isto normalmente acontece, quando o ultimo ticket negociado, foi enviado para o ativo de replay,

Estas funções acima, irão ficar executando até que o ultimo ticket seja enviado, ou até que o gráfico do ativo de replay seja fechado. Quando isto acontecer, teremos a execução da seguinte linha:

Replay.CloseReplay();

Esta irá encerrar definitivamente o replay.

Todo este código, é bastante bonito e simples de ser entendido. Mas você pode ter notado que existem vários pontos aqui, que se referem a uma classe, a C_Replay. Então vamos dar uma olhada nesta classe, e apesar do código dela ter muito em comum, com o encontrado nos artigos anteriores, tem uma parte que merece mais destaque. É justamente este que vamos olhar agora. No próximo tópico.


ViewReplay da classe C_Replay. Por que ele é tão importante ?

Este código pode ser visto logo abaixo:

long ViewReplay(void)
{
        m_IdReplay = ChartOpen(def_SymbolReplay, PERIOD_M1);
        ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");
        ChartRedraw(m_IdReplay);
        return m_IdReplay;
}

Você pode pensar : O que estas 4 linhas tem de tão importante assim, a ponto de permitir ou impedir que o replay seja criado ?!?! Apesar deste código ser bastante simples, ele é extremamente poderoso. E poderoso a ponto de evitar, que as coisas funcionem, mesmo que tudo pareça estar correto.

Então vamos entender ele. A primeira coisa que fazemos, é abrir um gráfico no nome do ativo de replay, e iniciamos o gráfico no período de tempo de 1 minuto. Mas você já viu nos dois artigos anteriores, que podemos mudar este tempo a qualquer momento que desejarmos.

Feito isto, carregamos um template bastante especifico, e aplicamos na janela do gráfico que acabou de ser aberto. É importante você notar, que este template é bastante especifico, não sendo qualquer um. Para criar este template, caso você o tenha deletado, ( ele estará no anexo ), você deverá compilar o EA, do sistema de replay de mercado, e aplicar este EA em um ativo qualquer. Logo depois, você deverá salvar este gráfico, como um template, e o nome que você deverá dar a este arquivo deverá ser: Market Replay, somente isto. Caso este arquivo não exista, ou o EA não esteja presente nele, todo o sistema irá falhar, não importa o que você tenha feito.

De certa forma, daria para resolver isto, se no lugar do EA, fosse usado um indicador. Neste caso iríamos chamar este indicador via MQL5 ( isto em tese ). Mas como disse no inicio deste artigo, tenho motivos em usar um EA no lugar do indicador. Então para resolver o problema de carregamento, da maneira o mais simples possível, usamos um template que contém o EA do sistema de replay.

Mas o simples fato de fazer isto, não garante muita coisa. Já que quando o EA for carregado, ele irá criar a variável global de terminal, dizendo para o serviço, que o sistema já esta preparado para funcionar. No entanto, os controles irão demorar um pouco para aparecer. Para agilizar um pouco a coisa, usamos uma chamada para forçar a atualização dos objetos no gráfico do ativo de replay.

Agora retornamos a id do gráfico do ativo de replay, já que não teremos como fazer isto em outro local, e precisamos desta informação para que o serviço, saiba quando o gráfico foi fechado.

Todas as demais funções da classe C_Replay, são bastante simples de serem compreendidas. Não merecendo muito mais destaque, aqui neste artigo.


Conclusão

Abaixo você pode ver o vídeo do sistema sendo carregado e como ele funciona na prática.



No próximo artigo, iremos criar o sistema de controle de posição, para possamos escolher em que momento o replay deverá ser iniciado. Então nos vemos lá.


Arquivos anexados |
Market_Replay.zip (10282.77 KB)
Ciência de dados e Aprendizado de Máquina (parte 10): Regressão de Ridge Ciência de dados e Aprendizado de Máquina (parte 10): Regressão de Ridge
A regressão de Ridge é uma técnica simples para reduzir a complexidade do modelo e evitar o ajuste excessivo que pode resultar da regressão linear simples
Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 02): Primeiros experimentos (II) Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 02): Primeiros experimentos (II)
Vamos experimentar uma outra abordagem, desta vez tentando alcançar o objetivo de 1 minuto. Mas isto não é uma tarefa tão simples, como muitos pensam.
Como desenvolver um sistema de negociação baseado no indicador Gator Oscillator Como desenvolver um sistema de negociação baseado no indicador Gator Oscillator
Um novo artigo em nossa série sobre como aprender a desenvolver um sistema de negociação baseado nos indicadores técnicos mais populares será sobre o indicador técnico Gator Oscillator e como criar um sistema de negociação por meio de estratégias simples.
Matrix Utils, estendendo as matrizes e a funcionalidade da biblioteca padrão de vetores Matrix Utils, estendendo as matrizes e a funcionalidade da biblioteca padrão de vetores
As matrizes servem como base para os algoritmos de aprendizado de máquina e computação em geral devido à sua capacidade de lidar efetivamente com grandes operações matemáticas. A biblioteca padrão tem tudo o que é necessário, mas vamos ver como podemos estendê-la introduzindo várias funções no arquivo utils, ainda não disponível na biblioteca