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

Desenvolvendo um sistema de Replay — Simulação de mercado (Parte 05): Adicionando Previas

MetaTrader 5Exemplos | 3 abril 2023, 15:06
335 4
Daniel Jose
Daniel Jose

Introdução

Em um artigo anterior. Para ser mais preciso, no artigo Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 02): Primeiros experimentos (II). Conseguimos desenvolver, uma forma de fazer com que o replay de mercado, fosse executado dentro de um tempo bastante realista e aceitável. Onde cada barra de 1 minuto, irá ser criada dentro deste 1 minuto. Ainda temos que fazer alguns pequenos ajustes, mas isto irá ficar para outro momento. Apesar de isto é algo extremamente necessário, para que possamos de fato pensar em um sistema capaz de fazer a simulação do replay de mercado.

Mas apesar de termos conseguido fazer isto, pelo menos aparentemente. O nosso replay de mercado, não é muito adequado, para que de fato, possamos fazer estudos. E de alguma forma praticar, um dado sistema operacional. Isto por que, não é possível fazer, ou melhor dizendo, ter uma pré-análise dos dados anteriores, ( seja minutos, horas ou dias ), desta forma não conseguimos de fato ter algo viável.

Quando digo pré-análise, estou falando em você ter, barras antes que o sistema de fato, venha a criar as barras, a partir de um ponto especifico. Sem estas barras anteriores, você não conseguirá fazer com que nenhum indicador, seja ele qual for, lhe diga algo valido.

Pense no seguinte: Você tem em arquivo todos os ticks de negociação executados em um dia qualquer. Mas apenas utilizando o conteúdo deste arquivo, você não irá conseguir, fazer com que nenhum indicador, lhe diga algo realmente útil. Mesmo que você utilize uma média móvel de 3 períodos, por exemplo, que é justamente a usada no sistema JOE DI NAPOLI. De fato, nenhuma indicação lhe será dada, seja ela de entrada ou saída, antes que no mínimo, 3 barras tenham sido criadas. Somente neste ponto, é que a média irá aparecer no gráfico. Em termos de praticidade, este sistema até o atual momento, é algo completamente inútil e inviável.

Pense só no fato, de você desejar fazer estudos em um tempo gráfico de 5 minutos. Você terá que esperar 15 minutos, para que a média móvel de 3 períodos, simplesmente venha a aparecer no gráfico. E mais outros tantos minutos, para que alguma indicação útil, possa de fato surgir. Ou seja, o sistema precisa ser atualizado, e a forma como fazer esta atualização, será o tema deste artigo.


Entendendo alguns detalhes

A parte da implementação, é algo bastante simples, e relativamente rápido de ser feito. Mas antes mesmo de criar qualquer linha de código, temos que pensar em um outro ponto.

O ponto a ser pensado, por mais estranho que possa parecer é : Qual o período gráfico, que irei usar no replay, e quais são os períodos mínimos, que cada indicador precisa, para gerar alguma informação útil ? Estas questões, são muito importantes, e precisam ser bem respondidas. Não existe de fato um modelo padrão, tudo irá depender de como você pretende fazer os seus estudos no replay de mercado.

Mas se estas questões, forem bem respondidas, você não ficará limitado somente ao replay. Você poderá também criar um simulador de mercado, que é algo extremamente, e muito mais interessante de ser feito. No entanto, ele ( simulador ) segue os mesmos princípios de funcionamento do replay. A única e real diferença entre o replay e o simulador, é a fonte dos dados ( TICKS ou BARRAS ) a serem utilizados para gerar o estudo.

No caso do replay, os dados são reais, já no caso do simulador, os dados são sintetizados de alguma forma. Seja com base em um padrão matemático, seja por pura e simples casualidade. E é aqui, onde mora algo bastante curioso no caso do simulador. Muitas instituições, e até mesmo operadores experientes, utilizam métodos matemáticos, para gerar um tipo especifico de estudo. Chamado de teste de stress, onde eles simulam movimentos de mercado, a ponto de conseguir prever possíveis movimentos extremos, em cenários o mais variados possíveis. Mas isto irá ficar para o futuro, já que envolve algumas coisas que ainda não expliquei, como fazer.

Mas não se preocupem, futuramente quando este sistema de replay estiver em um estagio mais avançado, irei explicar como você pode "criar" tais dados. Mas neste momento, iremos focar em como usar dados reais de mercado, ou seja um replay.

Para saber, qual o número de barras previas, que deveremos ter no nosso replay. Temos que executar um calculo bem simples:


onde N é o número de barras necessárias, P é o maior período que iremos usar, ou precisar para que uma média ou indicador inicie a fornecer alguma informação útil, T é a quantidade de minutos que existem no tempo gráfico. Para ficar mais claro, vejam os exemplos abaixo:

Exemplo 01:

Suponhamos que você irá utilizar, uma media móvel de 200 períodos no gráfico. E que irá usar um tempo gráfico de 5 minutos. Quantas barras, no mínimo, será preciso usar no gráfico ?

n = ( 200 * 5 ) + 5

isto é igual a 1005, ou seja teremos que ter no mínimo de 1005 barras de 1 minuto. Antes do replay se iniciar, para que a média de 200 seja plotada. Isto para usar o tempo gráfico igual ou inferior a 5 minutos. Assim a média irá aparecer logo no inicio do replay.

Exemplo 02:

Você deseja usar uma média móvel de 21 períodos, uma de 50 períodos, o RSI configurado em 14 períodos, e uma VWAP no intradiário, para efetuar seus estudos.  Fará o estudo, em um tempo gráfico de 5 minutos e 15 minutos. Mas ocasionalmente, irá olhar os 30 minutos, para confirmar as suas operações, seja de entrada ou saída. Quantas barras, no mínimo, você terá quer ter, antes mesmo de inicializar o replay ?

Para quem tem pouca experiência, esta situação parece muito complicada. Mas é algo simples. No entanto, é preciso pensar um pouco e ter atenção aos detalhes. Vamos entender como fazer o cálculo, para obter o numero mínimo de barras. Primeiramente, você deverá verificar qual é o maior período a ser usado nas médias ou indicadores. No caso o valor é de 50, já que a VWAP, é um indicador atemporal, não precisamos nos preocupar com ela. Este é o primeiro ponto. Agora vamos verificar, o tempo gráfico. Neste caso deveremos usar o tempo de 30, isto por que, ele é o maior entre os 3 que serão usados. Mesmo que o de 30, seja utilizado apenas uma única vez, deveremos utilizar ele como valor mínimo. Então o cálculo fica assim:

n = ( 50 * 30 ) + 30

isto é igual a 1530, ou seja teremos que ter, no mínimo de 1530 barras de 1 minuto, antes que o replay de fato seja iniciado.

Apesar de parecer muito, este numero não é nem perto, do que deveremos ter no caso mais extremo. No entanto, acredito que deu para sacar a ideia. Você sempre deverá ter uma quantidade muito maior de dados, do que muitos imaginam ser necessário, para efetuar estudos ou analises.

Agora vem uma outra questão. Normalmente na B3 ( Bolsa Brasileira ), o pregão ( Período de negociação ), se desenvolve em diversas etapas. E com uma janela diferente, para algumas classes de ativos. Normalmente os mercados futuros, iniciam as negociações as 9:00 e termina as 18:00. Mas existem momentos, durante o ano em que este período se estende até as 18:30. Já o mercado acionário, funciona entre 10:00 até as 18:00. Existem outros mercados, e ativos que tem horários próprios de negociação. Assim é preciso verificar estes parâmetros, antes mesmo de prosseguir. Mas tentem acompanhar a ideia. Por conta do fato da B3, ter uma janela de negociação, muitas vezes você terá que baixar dados, de vários dias, de forma a ter a quantidade mínima de barras, que foi calculada.

Nota Importante: Apesar de estamos tratado a coisa como sendo o numero de barras. Você não deve de maneira alguma se esquecer que as barras são geradas por ticks negociados, ou seja, a quantidade de dados necessários serão bem maiores. Mas para não gerar confusão, prefiro usar numero de barras para facilitar toda a explicação.

Então, precisamos de um outro cálculo. Este irá calcular a quantidade, mínima, de dias, a serem capturados. Isto é conseguido usando a seguinte formula:


onde N é o numero de dias, F é a hora inteira do momento de fechamento da janela de negociação, K é a hora inteira onde a janela de negociação se inicia, e M é o numero de minutos fracionários que existe dentro desta janela. Para exemplificar, vamos ver um exemplo de como fazer o cálculo:

O dólar futuro, iniciou suas negociações as 9:00 e terminou as negociações as 18:30. Quantas barras de 1 minuto, tivemos neste dia ?

n = (( 18 - 9 ) * 60 ) + 30

isto é igual a 570 barras de 1 minuto.

Normalmente este valor, é entorno de 540 barras de 1  minuto. Isto por conta que o mercado costuma fechar a janela de negociação as 18:00. Mas este numero não é exato, já que as negociações, podem sofrer alguma parada durante o dia, por conta de leilões, que podem acontecer no intradiário. O que torna a coisa um pouco mais complicada. Mas de uma forma ou de outra, você terá no máximo de 570 barras de 1 minuto por dia de negociação, já que os futuros, são os que tem a maior janela de negociação.

Bom sabendo disto, você divide a quantidade de barras necessárias, pela quantidade de barras diárias, que o ativo irá usar no replay. Esta quantidade diária, é uma média. Assim você terá a quantidade, mínima de dias, que deverão ser adquiridos, para que os dados das medias, e indicadores, sejam corretamente plotados no gráfico.


Implementação

A intenção aqui, é motivar você a aprender, a construir suas próprias soluções. Talvez eu possa vim a parecer repetitivo. Mas pequenas mudanças em um sistema, podem causar muitas diferenças. Já que teremos de usar um modelo de codificação, que tente manter o código o mais rápido quanto for possível. Mas ao mesmo tempo, sem que este, perca estabilidade. A primeira mudança, é vista logo abaixo:

#property service
#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
#include <Market Replay\C_Replay.mqh>
//+------------------------------------------------------------------+
input string    user00 = "WIN$N_M1_202108030900_202108051754";  //Arquivo de Barras ( Prev. )
input string    user01 = "WINQ21_202108060900_202108061759";    //Arquivo com ticks ( Replay )
//+------------------------------------------------------------------+
C_Replay        Replay;

Foi adicionada uma linha, que será a responsável por indicar qual o arquivo, que contem os dados prévios a serem utilizados. Futuramente, iremos mudar isto daqui, mas por enquanto, ficará desta forma. Não quero fazer você ficar louco, e sem entender como o código está sendo desenvolvido. Assim sendo, ele irá mudar aos pouco. Permitindo que você aprenda com ele.

O fato de podermos indicar, apenas e somente um único arquivo, implica que, se você tiver que utilizar vários dias, terá que fazer com que o MetaTrader 5, gere um arquivo com vários dias embutidos. Dizendo assim, parece complicado, mas é bem mais simples, e fácil de ser feito, do que pode parecer.


Notem, na figura acima, que as datas são diferentes. Com isto, teremos a captura de todas a barras necessárias, para que possamos fazer os nossos estudos. Atenção, ao fato de termos um total de 1605 barras de 1 minuto sendo capturadas, o que nos permite uma ampla faixa de trabalho. Já que nos exemplos de calculo, a quantidade era bem menor.

Mas por que temos que usar barras de 1 minuto ?!?! Por que não usar barras de 5 ou 15 minutos ?!?!

O motivo é que ao usar barras de 1 minuto, você terá a possibilidade de usar qualquer tempo gráfico. A própria plataforma MetaTrader 5, irá criar as barras, de tempo gráfico diferente para nos, oque é mão na roda, pois não precisaremos nos preocupar em fazer nenhum tipo de ajuste. Agora, se você utilizar uma base de dados, com barras de tempo de 15 minutos por exemplo, não conseguirá usar outros tempos gráficos de forma simples. Isto por que, você não irá poder contar com o auxilio que a plataforma MetaTrader 5, irá nos fornecer, ao usar barras de 1 minuto. Por conta disto, que você deverá sempre, e de preferencia, utilizar um tempo de barras de 1 minuto.

Apesar de tudo, até dá para fazer uso de uma base de dados de tempo gráfico diferente. Mas isto não será tratado agora. Veremos isto, quando formos ver as questões envolvendo a simulação.

Mas mesmo que você não queira capturar vários dias. Você pode capturar os dias separadamente, e depois unir os arquivos. Mas não se desesperem, ainda. Existe um outra solução, de forma a não precisar fazer as coisas assim. Mas não irei falar dela aqui, neste artigo. As próximas coisas a serem feita no código, podem ser vistas abaixo:

void OnStart()
{
        ulong t1;
        int delay = 3;
        long id;
        u_Interprocess Info;
        bool bTest = false;
        
        Replay.InitSymbolReplay();
        Replay.LoadPrevBars(user00);
        if (!Replay.LoadTicksReplay(user01)) return;
        Print("Aguardando permissão para iniciar replay ...");

Declaramos algumas variáveis para serem utilizadas pelo serviço. Logo depois fazemos a chamada, para definir o símbolo a ser usado no replay. É importante que esta chamada, seja a primeira coisa a ser de fato feita. Já que todo o restante, irá ser feito diretamente, dentro do símbolo criado.

Agora fazemos o carregamento das barras prévias. Vejam que existe uma sequencia lógica aqui. Mas esta função de carregamento de barras prévias, conta com alguns detalhes interessantes. Não totalmente explorados neste momento. Mas por hora, vamos continuar. Vendo como o sistema é inicializado.

Depois de carregado os valores prévios, caso eles existam. Fazemos o carregamento dos ticks negociados. Este é o ponto, que realmente iremos nos preparamos para o replay. Se tudo estiver correto, uma mensagem será imprimida no terminal, indicado que o sistema estará em modo de espera.

        id = Replay.ViewReplay();
        while (!GlobalVariableCheck(def_GlobalVariableReplay)) Sleep(750);
        Print("Permissão concedida. Serviço de replay já pode ser utilizado...");
        t1 = GetTickCount64();

Agora o sistema irá ficar neste modo de espera. Aguardando que o TEMPLATE de replay seja carregado. E o gráfico do ativo de replay, seja aberto e apresentado.

Quando isto acontecer, o laço de espera, irá ser encerrado. Já que neste caso uma variável global de terminal, terá sido criada, permitindo a comunicação entre o serviço de replay e o indicador de controle. Este indicador foi criado e pode ser estudado nos artigos Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 03): Ajustando as coisas (I) e Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 04): Ajustando as coisas (II).

Uma vez que o indicador de controle tenha sido carregado, é impressa uma mensagem no console. Agora o usuário já poderá dar play no sistema. E por último, fazemos a captura do numero de ticks de maquina. Para gerar uma forma de verificar o tempo decorrido.

Feito isto entramos no laço, que já foi explicado com detalhes nos artigos anteriores. Mas esta foi apenas a primeira parte da implementação. Temos outras coisas para serem vista e que merecem uma atenção especial.


Uma nova classe C_Replay

Apesar de parecer estranho, foi necessário fazer algumas mudanças internas, na classe responsável por manter o replay. Irei detalhar as novas mudanças, com calma, para que você possam compreender, o que e por que elas ocorreram. Vamos começar pelas variáveis que agora são outras:

int      m_ReplayCount;
datetime m_dtPrevLoading;
long     m_IdReplay;
struct st00
{
        MqlTick Info[];
        int     nTicks;
}m_Ticks;

Este novo conjunto de variáveis, é suficiente para o nosso trabalho. Se você olhar o código anterior, iram notar que agora o conjunto é diferente. Por conta disto, outras coisas também mudaram. Agora temos uma nova rotina, para inicializar o símbolo de replay.

void InitSymbolReplay(void)
{
        SymbolSelect(def_SymbolReplay, false);
        CustomSymbolDelete(def_SymbolReplay);
        CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay), _Symbol);
        SymbolSelect(def_SymbolReplay, true);
}

Começamos retirando, caso ele esteja presente, o símbolo de replay da janela de observação de mercado. Isto de fato só irá acontecer, caso o símbolo não esteja em um gráfico aberto.

Depois disto, apagamos o mesmo, para enfim criá-lo, como um ativo customizado. Mas por que desta trabalheira toda ?!?! Calma, Depois você entenderá o motivo disto. Depois que o ativo customizado foi criado, colocamos ele na janela de observação de mercado, para assim podermos manipulá-lo, e colocar ele em um gráfico. Este procedimento de inicialização, futuramente terá que sofrer mudanças. Mas por hora é o suficiente para o que queremos e precisamos fazer. Lembre-se temos que fazer as coisas funcionarem primeiro, antes de modificar as mesmas.

A próxima rotina que sofreu mudanças, é a de carregamento dos tickets negociados.

#define macroRemoveSec(A) (A - (A % 60))
                bool LoadTicksReplay(const string szFileNameCSV)
                        {
                                int     file,
                                        old;
                                string  szInfo;
                                MqlTick tick;
                                
                                if ((file = FileOpen("Market Replay\\Ticks\\" + szFileNameCSV + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)
                                {
                                        Print("Carregando ticks de replay. Aguarde...");
                                        ArrayResize(m_Ticks.Info, def_MaxSizeArray);
                                        old = m_Ticks.nTicks = 0;
                                        for (int c0 = 0; c0 < 7; c0++) FileReadString(file);
                                        while ((!FileIsEnding(file)) && (m_Ticks.nTicks < def_MaxSizeArray))
                                        {
                                                szInfo = FileReadString(file) + " " + FileReadString(file);
                                                tick.time = macroRemoveSec(StringToTime(StringSubstr(szInfo, 0, 19)));
                                                tick.time_msc = (int)StringToInteger(StringSubstr(szInfo, 20, 3));
                                                tick.bid = StringToDouble(FileReadString(file));
                                                tick.ask = StringToDouble(FileReadString(file));
                                                tick.last = StringToDouble(FileReadString(file));
                                                tick.volume_real = StringToDouble(FileReadString(file));
                                                tick.flags = (uchar)StringToInteger(FileReadString(file));
                                                if ((m_Ticks.Info[old].last == tick.last) && (m_Ticks.Info[old].time == tick.time) && (m_Ticks.Info[old].time_msc == tick.time_msc))
                                                        m_Ticks.Info[old].volume_real += tick.volume_real;
                                                else
                                                {
                                                        m_Ticks.Info[m_Ticks.nTicks] = tick;
                                                        m_Ticks.nTicks += (tick.volume_real > 0.0 ? 1 : 0);
                                                        old = (m_Ticks.nTicks > 0 ? m_Ticks.nTicks - 1 : old);
                                                }
                                        }
                                }else
                                {
                                        Print("Aquivo de ticks ", szFileNameCSV,".csv não encontrado...");
                                        return false;
                                }
                                return true;
                        };
#undef macroRemoveSec

Primeiramente, tentamos abrir o arquivo, que deverá conter os tickets de negócios executados. Atenção a este ponto: No momento, ainda não estamos fazendo testes de verificação. Então tome cuidado, para indicar o arquivo correto.

O arquivo deverá estar localizado na pasta indicada dentro da área do MQL5. Mas você, não deverá indicar a extensão do arquivo, ela é por padrão a extensão CSV. Caso o arquivo seja encontrado, iremos começar o carregamento. Caso ele não seja encontrado, o serviço de replay não poderá executar, e uma mensagem é postada na caixa de mensagens, informando o motivo do sistema ter falhado.

Vamos ver o que acontece, no caso do arquivo de tickets negociados, seja encontrado. Neste caso, iremos primeiramente pular o conteúdo da primeira linha do arquivo. Que para nos, esta linha não tem nenhuma serventia no momento. Depois disto, entraremos em um loop para ler as informações. O que fazemos primeiro, é capturar a data e o horário em que o ticket foi negociado, para logo depois guardar as informações em uma posição temporária. Depois, capturamos cada uma dos valores na ordem correta. Notem que não precisamos indicar coisa alguma, a própria formatação do arquivo CSV, aliada a forma como o MQL5 faz a leitura, já nos bastará para que os dados sejam lidos corretamente.

Agora fazemos um teste, para verificar a seguinte condição: Se o preço anterior do último negócio, for o mesmo do negócio atual, e o tempo também forem iguais, o volume atual, será somado ao volume anterior. Mas para que isto aconteça, ambos os testes tem que ser iguais.

Observem que não estou preocupado, com a questão do flag, ou seja, não importa se um foi compra e o outro venda. Se as demais informações forem iguais, isto não irá afetar o replay, pelo menos neste momento. Já que o preço, simplesmente não se moveu. Agora caso a condição de testes venha a falhar, teremos a indicação de que estamos lendo um ticket diferente. Neste caso vamos adicioná-lo a nossa matriz de tickets. Agora um detalhe: Caso o ticket seja um ajuste no BID ou ASK, não haverá volume nele. Então não iremos adicionar uma nova posição, e esta posição será sobrescrita, quando um novo ticket for lido. Caso tenhamos algum volume, a posição será incrementada.

E ficamos neste laço, até que o fim do arquivo, ou o limite de tickets seja alcançado. Mas cuidado, pois não estamos fazendo nenhum teste ainda, estes ajudam a evitar certas falhas. Mas já que o sistema ainda esta bastante simples, e a quantidade de dados a serem utilizados ainda é pequena. Podemos deixar algumas pequenas falhas passarem. Mas com o tempo esta rotina, irá conter um incremento na quantidade de testes, a fim de evitar problemas, que por enquanto, não nos custa muito.

A próxima rotina na lista, tem seu código exposto logo abaixo, para que possamos analisá-lo com calma.

bool LoadPrevBars(const string szFileNameCSV)
{
        int     file,
                iAdjust = 0;
        datetime dt = 0;
        MqlRates Rate[1];
                                
        if ((file = FileOpen("Market Replay\\Bars\\" + szFileNameCSV + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)
        {
                for (int c0 = 0; c0 < 9; c0++) FileReadString(file);
                Print("Carregando barras previas para Replay. Aguarde ....");
                while (!FileIsEnding(file))
                {
                        Rate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));
                        Rate[0].open = StringToDouble(FileReadString(file));
                        Rate[0].high = StringToDouble(FileReadString(file));
                        Rate[0].low = StringToDouble(FileReadString(file));
                        Rate[0].close = StringToDouble(FileReadString(file));
                        Rate[0].tick_volume = StringToInteger(FileReadString(file));
                        Rate[0].real_volume = StringToInteger(FileReadString(file));
                        Rate[0].spread = (int) StringToInteger(FileReadString(file));
                        iAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);
                        dt = (dt == 0 ? Rate[0].time : dt);
                        CustomRatesUpdate(def_SymbolReplay, Rate, 1);
                }
                m_dtPrevLoading = Rate[0].time + iAdjust;
                FileClose(file);
        }else
        {
                Print("Falha no acesso ao arquivo de dados das barras previas.");
                m_dtPrevLoading = 0;                                    
                return false;
        }
        return true;
}

O que este código acima faz, é justamente carregar para o replay, todas a barras prévias necessárias, ou pertencentes a um determinado arquivo. Mas diferente da rotina anterior, onde havia a necessidade de guardar as informações para uso posterior. Aqui fazemos diferente. Vamos lendo as informações, e adicionado elas logo em seguida ao ativo. Então acompanhem a explicação, para entender como isto acontece.

Primeiramente tentamos abrir o arquivo de barras. Notem que temos o mesmo comando, que é usado na versão de tickets. Só que agora, a localização é outra, e o conteúdo do arquivo, também é outro. Mas a forma de acessar as coisas será bastante parecida. Caso tenhamos sucesso nesta abertura, começaremos pulando a primeira linha do arquivo. Pois ela não nos interessa no momento.

E desta vez, entramos em um laço que somente irá terminar, na última linha do arquivo. Não existe limite para o tamanho ou quantidade de dados presentes neste arquivo. Todos os dados irão ser lidos. Os dados lidos, seguem a modelagem OHCL, então colocamos eles em uma estrutura temporária. Feito isto, temos um ponto que precisamos prestar bastante atenção. Como poderemos saber, em que ponto começa o replay, e onde termina as barras prévias ?!?!

Fora desta rotina, seria algo um pouco complicado de você saber. Mas aqui temos a indicação exata, de onde termina as barras prévias, e onde começa o replay. É justamente neste ponto, em que armazenaremos esta posição temporal, para uso posterior. Agora reparem no fato de que, a cada linha de barras lidas, iremos imediatamente, atualizar as barras contidas no ativo de replay. Desta forma não teremos nenhum trabalho extra depois. Com isto no momento em que as barras prévias, tiverem sido lidas, já poderemos adicionar os nossos indicadores.

Vamos ver a próxima rotina, que é bem simples. Porém muito importante.

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

O que esta rotina faz, é abrir um gráfico do ativo de replay, no tempo gráfico padrão de 1 minuto, você pode usa um outro tempo gráfico padrão, isto para não ficar toda hora, tento que trocar ele.

Feito isto, carregamos um template, que deverá obrigatoriamente conter o indicador de replay, caso este indicador não esteja presente neste template, você ficará impossibilitado de dar play no serviço. Apesar de tudo, você pode usar um outro template, mas ao fazer isto, você terá que adicionar de forma manual o indicador de replay no gráfico do ativo, que esta sendo usando para executar o replay. Se você não se incomodar em fazer isto, tudo bem, fique a vontade. Mas ao usar o template com o indicador, você já terá acesso imediato ao sistema, já que forçaremos uma atualização do gráfico e retornamos o index do gráfico para o serviço, poder observar se ele esta ou não disponível.

Assim como inicializamos o sistema, temos que fechar o mesmo, e para isto cotamos com uma rotina própria.

void CloseReplay(void)
{
        ChartClose(m_IdReplay);
        SymbolSelect(def_SymbolReplay, false);
        CustomSymbolDelete(def_SymbolReplay);
        GlobalVariableDel(def_GlobalVariableReplay);
}

O grande detalhe aqui, é que a ordem, em que as coisas são feitas, importa e muito. Caso você mude a ordem, as coisas podem não a acontecer conforme o esperado. Então primeiramente, fechamos o gráfico do ativo, removemos ele da janela de observação do mercado, deletamos ele da lista de símbolos customizados e finalmente apagamos a variável global de terminal. Com isto o serviço de replay irá ser encerrado.

A próxima rotina que também sofreu algumas mudanças.

inline int Event_OnTime(void)
{
        bool    bNew;
        int     mili, iPos;
        u_Interprocess Info;
        static MqlRates Rate[1];
        static datetime _dt = 0;
                                
        if (m_ReplayCount >= m_Ticks.nTicks) return -1;
        if (bNew = (_dt != m_Ticks.Info[m_ReplayCount].time))
        {
                _dt = m_Ticks.Info[m_ReplayCount].time;
                Rate[0].real_volume = 0;
                Rate[0].tick_volume = 0;
        }
        mili = (int) m_Ticks.Info[m_ReplayCount].time_msc;
        do
        {
                while (mili == m_Ticks.Info[m_ReplayCount].time_msc)
                {
                        Rate[0].close = m_Ticks.Info[m_ReplayCount].last;
                        Rate[0].open = (bNew ? Rate[0].close : Rate[0].open);
                        Rate[0].high = (bNew || (Rate[0].close > Rate[0].high) ? Rate[0].close : Rate[0].high);
                        Rate[0].low = (bNew || (Rate[0].close < Rate[0].low) ? Rate[0].close : Rate[0].low);
                        Rate[0].real_volume += (long) m_Ticks.Info[m_ReplayCount].volume_real;
                        bNew = false;
                        m_ReplayCount++;
                }
                mili++;
        }while (mili == m_Ticks.Info[m_ReplayCount].time_msc);
        Rate[0].time = m_Ticks.Info[m_ReplayCount].time;
        CustomRatesUpdate(def_SymbolReplay, Rate, 1);
        iPos = (int)((m_ReplayCount * def_MaxPosSlider) / m_Ticks.nTicks);
        GlobalVariableGet(def_GlobalVariableReplay, Info.Value);
        if (Info.s_Infos.iPosShift != iPos)
        {
                Info.s_Infos.iPosShift = iPos;
                GlobalVariableSet(def_GlobalVariableReplay, Info.Value);
        }
        return (int)(m_Ticks.Info[m_ReplayCount].time_msc < mili ? m_Ticks.Info[m_ReplayCount].time_msc + (1000 - mili) : m_Ticks.Info[m_ReplayCount].time_msc - mili);
}

O que esta rotina acima faz, é criar uma barra de 1 minuto, e postar esta barra no ativo de replay. Mas aqui temos algo, que no momento pode afetar um pouco o seu entendimento, de por que o replay, esta mostrando algumas coisas, e outras não.

Observem que o volume de tickets, será sempre zero, e por que disto ?!?! Para entender, veja o documento Ticks Reais e Gerados. Se você leu e não entendeu o documento, ou não entendeu o por que o volume de tickets, é sempre zero, você esta ignorando um fato. Então vamos tentar entender o por que, deste valor sempre estará em zero.

Quando lemos os dados de tickets negociados, estamos lendo os tickets reais, ou seja, o volume mostrado ali, é real. Um exemplo: Caso uma ordem tenha um volume igual a 10, isto indica que ali houve uma negociação com um volume real de 10 volumes mínimos exigidos, e não 10 tickets foram usados. Não é possível gerar o volume de 1 ticket negociado, mas a partir dai, temos algo que de fato gera um volume de tickets, que é quando uma ordem de abertura e fechamento acontece, teremos como resultado um volume de 2 tickets. Mas na prática, isto de fato não acontece, o que na prática acontece, é que teremos um volume mínimo de tickets igual a 3. Mas já que estamos usando tickets de negociação real, temos que fazer um ajuste no valor a ser apresentado, e no caso não adicionei ainda este calculo no sistema de replay, então apenas o valor real negociado, é que será de fato apresentado.

É preciso tomar cuidado com isto, pois apesar de parecer ser a mesma coisa, o volume real, indica quantos negócios de fato aconteceram, já o volume de ticks indicada quantos movimentos aconteceram.

Sei que isto neste momento parece muito confuso e de difícil compreensão, mas em artigos futuros, quando formos tratar do mercado de forex, e sim, iremos ver como fazer replay e simulações no mercado de forex, isto ficará mais claro e melhor explicado. Então não fique esquentando a cabeça, neste momento entanto tentando entender esta questão. Futuramente isto será melhor compreendido.

A última rotina a ser vista neste artigo, é mostrada logo abaixo:

int AdjustPositionReplay()
{
        u_Interprocess Info;
        MqlRates Rate[1];
        int iPos = (int)((m_ReplayCount * def_MaxPosSlider * 1.0) / m_Ticks.nTicks);
                                
        Info.Value = GlobalVariableGet(def_GlobalVariableReplay);
        if (Info.s_Infos.iPosShift == iPos) return 0;
        iPos = (int)(m_Ticks.nTicks * ((Info.s_Infos.iPosShift * 1.0) / def_MaxPosSlider));
        if (iPos < m_ReplayCount)
        {
                CustomRatesDelete(def_SymbolReplay, m_dtPrevLoading, LONG_MAX);
                m_ReplayCount = 0;
                if (m_dtPrevLoading == 0)
                {
                        Rate[0].close = Rate[0].open = Rate[0].high = Rate[0].low = m_Ticks.Info[m_ReplayCount].last;
                        Rate[0].tick_volume = 0;
                        Rate[0].time = m_Ticks.Info[m_ReplayCount].time - 60;
                        CustomRatesUpdate(def_SymbolReplay, Rate, 1);
                }
        };
        for (iPos = (iPos > 0 ? iPos - 1 : 0); m_ReplayCount < iPos; m_ReplayCount++) Event_OnTime();
        return Event_OnTime();
}

Nesta rotina, temos apenas um ponto diferente dos artigos anteriores, e este ponto, irá fazer a correção, para que possamos iniciar a colocação dos tickets negociados. Desta forma, tudo que estiver acima deste ponto, é considerado como sendo replay de tickets de negócios reais, e antes deste ponto, os valores são de barras prévias, se apenas informássemos um valor igual a zero neste ponto, todas as informações gravadas no ativo de replay seriam removidas. Você teria que efetuar a leitura novamente das barras de 1 minuto, de dentro dos dados, que indicariam os valores de barras prévias. Resumindo: Seria um trabalho totalmente desnecessário, além de muito mais complicado de ser feito. Então tudo que precisamos fazer, é indicar o ponto, onde este limiar acontece e o MetaTrader 5, irá remover as barras que foram criadas pelos tickets reais, facilitando assim em muito a nossa vida.


Conclusão

Todo o restante da rotina já foi explicada no artigo Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 04): Ajustando as coisas (II), então não irei entrar em detalhes novamente aqui.

No vídeo abaixo, você pode ver o sistema funcionando, onde será demonstrado como é possível adicionar indicadores diversos ao sistema de replay.




Arquivos anexados |
Market_Replay.zip (6102.2 KB)
Últimos Comentários | Ir para discussão (4)
napalermo
napalermo | 6 abr 2023 em 03:49

Olá Daniel, primeiro lugar te parabenizar, cada dia você se supera mais. rsrsrs

o conteúdo que traz é riquíssimo, com técnica e excelência na criação de cada linha. sempre procurando fazer da melhor forma. 

Parabéns mesmo. 

Rapaz eu confesso que a programação em mql5 esta sendo muito difícil. eu consigo me virar em visual.net com o básico.

mas aqui não consegui fazer nada mais "sério". tentei até pegar a parte do tape reading do seu projeto mas não funcionou aqui. acredito que tenho que fazer e rever o projeto inteiro. 

é que na correria a gente sempre acaba tentando só a parte que queremos e ai vem os problemas. rsrsr.


estou tentando fazer um EA. mas estou com problema numa variavel, gostaria de saber se pode me ajudar. 

nessa parte do codigo passa   

IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, 100);

mas nessa parte da erro dizendo que tem que ser double

    IndicatorSetDouble(INDICATOR_CALCULATIONS, 1, (double) open_price);

    IndicatorSetDouble(INDICATOR_CALCULATIONS, 2, (double) high_price);

    IndicatorSetDouble(INDICATOR_CALCULATIONS, 3, (double) low_price);

    IndicatorSetDouble(INDICATOR_CALCULATIONS, 4, (double) close_price);

as variaveis foram declaradas como double até forcei ai mas continua dando erro. 

ja tentei INDICATOR_DATA no lugar do INDICATOR_CALCULATIONS e continua dando erro.

pedi para mostrar na tela e as variaveis estão carregando corretamente com valores double.

33573.43 33573.44 33569.979999999996 33569.99


desculpe incomodar, nem sei se poderia colocar aqui

se puder me ajudar ficarei muito grato. 

Anderson.

Daniel Jose
Daniel Jose | 8 abr 2023 em 13:44
napalermo #:

Olá Daniel, primeiro lugar te parabenizar, cada dia você se supera mais. rsrsrs

o conteúdo que traz é riquíssimo, com técnica e excelência na criação de cada linha. sempre procurando fazer da melhor forma. 

Parabéns mesmo. 

Rapaz eu confesso que a programação em mql5 esta sendo muito difícil. eu consigo me virar em visual.net com o básico.

mas aqui não consegui fazer nada mais "sério". tentei até pegar a parte do tape reading do seu projeto mas não funcionou aqui. acredito que tenho que fazer e rever o projeto inteiro. 

é que na correria a gente sempre acaba tentando só a parte que queremos e ai vem os problemas. rsrsr.


estou tentando fazer um EA. mas estou com problema numa variavel, gostaria de saber se pode me ajudar. 

nessa parte do codigo passa   

IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, 100);

mas nessa parte da erro dizendo que tem que ser double

    IndicatorSetDouble(INDICATOR_CALCULATIONS, 1, (double) open_price);

    IndicatorSetDouble(INDICATOR_CALCULATIONS, 2, (double) high_price);

    IndicatorSetDouble(INDICATOR_CALCULATIONS, 3, (double) low_price);

    IndicatorSetDouble(INDICATOR_CALCULATIONS, 4, (double) close_price);

as variaveis foram declaradas como double até forcei ai mas continua dando erro. 

ja tentei INDICATOR_DATA no lugar do INDICATOR_CALCULATIONS e continua dando erro.

pedi para mostrar na tela e as variaveis estão carregando corretamente com valores double.

33573.43 33573.44 33569.979999999996 33569.99


desculpe incomodar, nem sei se poderia colocar aqui

se puder me ajudar ficarei muito grato. 

Anderson.

Duas coisas:

Primeiro: Quando for postar um código, faça isto usando ALT + S, assim o código fica mais evidente para que possamos entender que é código e não comentário ... A coisa toda irá se apresentar da seguinte forma para todos:

IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, 100);

Assim ficará bem mais simples de entender ...👍

Segundo: Você está tentando fazer um código de Expert Advisor ou um Indicador ?!!?🤨🤨 ... Pois não entendi o que você está tentando fazer ...

napalermo
napalermo | 10 abr 2023 em 04:33
Daniel Jose #:

Duas coisas:

Primeiro: Quando for postar um código, faça isto usando ALT + S, assim o código fica mais evidente para que possamos entender que é código e não comentário ... A coisa toda irá se apresentar da seguinte forma para todos:

Assim ficará bem mais simples de entender ...👍

Segundo: Você está tentando fazer um código de Expert Advisor ou um Indicador ?!!?🤨🤨 ... Pois não entendi o que você está tentando fazer ...


ok, 

IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, 100);

mas nessa parte da erro dizendo que tem que ser double

    IndicatorSetDouble(INDICATOR_CALCULATIONS, 1, (double) open_price);

    IndicatorSetDouble(INDICATOR_CALCULATIONS, 2, (double) high_price);

    IndicatorSetDouble(INDICATOR_CALCULATIONS, 3, (double) low_price);

    IndicatorSetDouble(INDICATOR_CALCULATIONS, 4, (double) close_price);

ja tentei INDICATOR_DATA no lugar do INDICATOR_CALCULATIONS e continua dando erro.

pedi para mostrar na tela e as variaveis estão carregando corretamente com valores double.

33573.43 33573.44 33569.979999999996 33569.99


ok, obrigado pela ajuda vou sempre fazer isso com códigos. 

estou criando um EA.

na realidade é para ser um gráfico atemporal montado por preço.

Daniel Jose
Daniel Jose | 10 abr 2023 em 14:19
napalermo #:


ok, 

ja tentei INDICATOR_DATA no lugar do INDICATOR_CALCULATIONS e continua dando erro.

pedi para mostrar na tela e as variaveis estão carregando corretamente com valores double.

33573.43 33573.44 33569.979999999996 33569.99


ok, obrigado pela ajuda vou sempre fazer isso com códigos. 

estou criando um EA.

na realidade é para ser um gráfico atemporal montado por preço.

Leia esta parte da documentação : https://www.mql5.com/pt/docs/runtime/running, isto vai ajudar a você entender por que esta dando errado ...😁👍

Algoritmos de otimização populacionais: Busca por cardume de peixes (FSS - Fish School Search) Algoritmos de otimização populacionais: Busca por cardume de peixes (FSS - Fish School Search)
O FSS (Fish School Search) é um algoritmo avançado de otimização inspirado no comportamento dos peixes que nadam em cardumes. Aproximadamente 80% desses peixes nadam em comunidades organizadas de parentes, o que tem sido comprovado como uma estratégia importante para melhorar a eficiência de procura por alimento e proteção contra predadores.
Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 04): Ajustando as coisas (II) Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 04): Ajustando as coisas (II)
Vamos continuar a criação do sistema e controle. Já que sem uma forma de controlar o serviço, fica muito complicado dar algum outro passo a fim de melhorar algo no sistema.
DoEasy. Controles (Parte 28): Estilos de barra no controle ProgressBar DoEasy. Controles (Parte 28): Estilos de barra no controle ProgressBar
Neste artigo veremos estilos de exibição e texto descritivo para o controle ProgressBar.
Teoria das Categorias em MQL5 (Parte 2) Teoria das Categorias em MQL5 (Parte 2)
A Teoria das Categorias é um ramo diverso da Matemática e em expansão, sendo uma área relativamente recente na comunidade MQL5. Esta série de artigos visa introduzir e examinar alguns de seus conceitos com o objetivo geral de estabelecer uma biblioteca aberta que atraia comentários e discussões enquanto esperamos promover o uso deste campo notável no desenvolvimento da estratégia dos traders.