English Русский 中文 Español Deutsch 日本語
preview
Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 17): Tiquete e mais tiquetes (I)

Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 17): Tiquete e mais tiquetes (I)

MetaTrader 5Exemplos | 29 junho 2023, 11:37
243 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior, Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 16): Um novo sistema de classes, foi feita as mudanças necessárias na classe C_Replay. Tais mudanças tem como intuito, simplificar diversas coisas que precisaremos fazer. Então a classe C_Replay, que antes era uma classe grande e inchada, passou por um processo de enxugamento, onde a sua complexidade foi distribuída entre outras classe. Desta maneira, se tornou muito mais simples, e fácil implementar novas funcionalidades, e trazer melhorias para dentro do sistema de replay / simulador. Tais melhorias começarão a ser vista a partir deste artigo, e irão se estender por outros sete artigos que irão vim na sequência.

A primeira das questões que iremos tratar, é extremamente complicada de ser modelada. Isto de uma maneira, que todos possam entender, apenas estudando o código. Sabendo disto, gostaria que você, preste-se a devida atenção as explicações, que irão ser dadas durante o decorrer destes artigos. Se você fizer isto, irá conseguir acompanhar a coisa, pelo menos é isto que espero que aconteça. Pois o assunto de fato é bastante denso e complexo. Digo isto, pois o que iremos fazer a partir deste artigo, pode em alguns casos ser desnecessário, pelo ponto de vista de alguns. Mas para outros, isto será de suma importância. Apesar de ser algo bastante custoso de ser feito. Irei fazer, e apresentar os detalhes, em etapas, para que você consiga acompanhar a linha de raciocínio, e não fique perdido.

A grande questão, é que durantes todos os artigos anteriores, o foco foi apenas e somente, na geração de um gráfico. E este deveria ser plotado, de maneira que o ativo de replay / simulação, se comporta-se de uma forma bastante parecida, com o que acontece no mercado real. Sei que tem muitas pessoas, que operam utilizando algumas outras coisas, como por exemplo o Book de ofertas. Apesar de, pessoalmente, não considero uma boa pratica, fazer uso de tal instrumento. Muitos acreditam que existe alguma correlação, entre o que acontece no book, com o que esta sendo negociado. Tudo bem, cada um vê as coisas como deseja. Mas independentemente disto, existe sim uma ferramenta, que muitos utilizam em suas negociações, que é o gráfico de tickets. Se você não sabe do que se trata veja a imagem da figura 01.


Figura 01

Figura 01 - Gráfico de Tickets

Este gráfico, aparece em diversos pontos na plataforma MetaTrader 5. Para que você tenha uma noção dos locais, posso citar alguns, que fazem parte da versão padrão do MetaTrader 5. São eles: A janela de Observação de mercado, como pode ser visto na figura 01. No book ( figura 02 ) e também no sistema de ordens ( figura 03 ).

Além destes locais, você também pode fazer uso de algum indicador, a fim de ver estas mesmas informações. Exemplo disto pode ser visto em: Desenvolvendo um EA de negociação do Zero ( Parte 13 ) : Times And Trade ( II ). Todos estes sistemas precisam, que o sistema que estamos desenvolvendo, consiga informar ou repassar as informações dos tickets, de uma forma adequada. Mas não é bem a informação sobre os tickets que você vê, de fato em todas estas figuras. O que você de fato esta vendo, é a variação nos valores de preço ASK e BID. É isto que esta sendo de fato mostrado. 


Figura 02

Figura 02 - Gráfico de tickets visto no book


Figura 03

Figura 03 - Gráfico de tickets visto no sistema de ordens.


É importante você se atentar e entender este fato. Mas independentemente disto, não quero que o sistema, que esta sendo desenvolvido, não conte com tais informações. O motivo disto é que a ideia, é promover uma experiência, que seja a mais próxima quanto for possível de um mercado real. E tal informação é de certa forma, válida e precisa estar presente. Mesmo que o usuário do sistema, não venha de fato a fazer uso da mesma. Não quero que ele pense, que não é possível desenvolver tal coisa. É verdade que fazer isto, não é uma das tarefas mais simples. Para dizer a verdade, a coisa é bem mais complicada do que parece e você vai logo entender o motivo. Isto conforme a explicação for sendo feita. O quanto tal tarefa é complicada, e cheia de pequenos detalhes. Alguns deles bastante exóticos por assim dizer.

Aqui iremos começar a implementar este sistema. Mas iremos começar da forma o mais simples quanto for possível ser feita. Primeiramente, iremos fazer com que ele apareça na janela de observação de mercado ( Figura 01 ). A partir dai, iremos tentar fazer com que ele venha a aparecer em outros locais também. Mas o simples fato de fazer com que ele apareça na janela de observação de mercado, já será uma tarefa bem complicada. Ao mesmo tempo interessante. Já que quando isto for feito, e estivermos usando a simulação de movimentos dentro de uma barra de 1 minuto, poderemos ao observar o gráfico de tickets na janela de observação de mercado, ver como o RANDOM WALK, foi criado pelo simulador. Isto é algo bastante curioso e interessante de ser de fato visto.

Mas vamos com calma. Pois apesar de a coisa aparentar ser simples de ser construída, não encontrei referencias que pudessem de fato me ajudar. Isto na questão da implementação, a fim de permitir a simplificar as coisas, ou algum passo a ser dado. Por conta que basicamente, a única referencia que encontrei, pesquisando em diversos locais, foi a própria documentação do MQL5, e nada muito além disto. E mesmo esta documentação, não deixa muito claro alguns detalhes. O que irei expor aqui, neste e nos próximos artigos, é o que de fato consegui entender, para ao implementar o sistema. Me desculpem aqueles que por ventura, entendem o sistema de outra forma, ou tem mais experiência no assunto. Apesar de minhas tentativas, a única forma de realmente fazer a coisa funcionar, foi a que será mostrada. Então dicas ou sugestões de outras formas, serão muito bem aceitas, desde que seja de fato funcional.

Vamos começar a implementar a coisa mais maluca de todas. Por conta do grau de loucura envolvidos. Neste sistema que será implementado. Nestes primeiros momentos não iremos utilizar dados simulados. Então no arquivo em anexo, você encontrará dados REAIS de 2 dias em 4 ativos diferentes. Isto para que você possa ter, pelo menos uma base para experimentação. Não precisa confiar em mim, muito pelo contrário. Quero que você mesmo faça a captura dos dados reais do mercado, e os experimente no sistema. Desde maneira você poderá tirar as suas próprias conclusões, sobre o que de fato esta acontecendo. Isto antes de que venhamos a implementar um sistema de simulação. Pois de fato, a coisa é muito mais maluca do que pode vim a se apresentar neste primeiro momento.


Implementando a primeira versão

Nesta primeira versão, alguns dos recursos serão desativados. Isto por conta de que não quero, que você fique apenas acreditando, que o código esta totalmente correto. Pois no bem da verdade, existe uma falha nele, que é com relação ao temporizador. Isto pode ser notado ao testar os dados reais, que estarão no anexo. Mas é algo que neste momento, até dá para passar, por que não traz nenhum prejuízo ao processo em si. Apenas faz com que o tempo de montagem das barras de 1 minuto, não seja exatamente como aconteceu no mercado real.

Mas vamos começar, fazendo uma pequena mudança, no arquivo do serviço:

#property service
#property icon "\\Images\\Market Replay\\Icon.ico"
#property copyright "Daniel Jose"
#property version   "1.17"
#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/11106"
//+------------------------------------------------------------------+
#define def_Dependence  "\\Indicators\\Market Replay.ex5"
#resource def_Dependence
//+------------------------------------------------------------------+
#include <Market Replay\C_Replay.mqh>
//+------------------------------------------------------------------+
input string            user00 = "Mini Indice.txt";     //Arquivo de configuração do Replay.
input ENUM_TIMEFRAMES   user01 = PERIOD_M5;             //Tempo gráfico inicial.
//input bool            user02 = false;                 //Visualizar a construção das barras. ( Bloqueado Temporariamente )
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(false, user03));
        }
        delete pReplay;
}
//+------------------------------------------------------------------+

Esta linha foi travada, por conta que existem alguns detalhes que precisaram ser modificados, para que o processo de visualização da montagem das barra, seja feito de forma adequada. Por conta disto, o processo de visualização, no caso de um avanço rápido, não será visível. Para garantir isto, passamos o argumento correspondente como sendo um valor falso, ou zero.

Esta é a primeira coisa que precisamos fazer. Agora teremos que fazer mais algumas pequenas mudanças. A partir de agora, a coisa pode começar a ficar um pouco confusa, para quem está vendo este artigo antes de ter lido os demais. Se isto acontecer, aconselho que você pare neste momento a leitura deste artigo, e procure começar lendo a a partir do primeiro artigo desta serie: Desenvolvendo um sistema de Replay - Simulação de mercado ( Parte 01 ) - Primeiros experimentos ( I ), pois entender o que estava acontecendo, irá lhe ajudar a entender, o que irá acontecer, a partir deste ponto.

Dado este conselho, vamos prosseguir. A primeira coisa que faremos agora, será mudar a rotina de leitura do arquivo, que contém os tickets reais. A rotina original pode ser vista abaixo.

inline bool ReadAllsTicks(void)
                        {
#define def_LIMIT (INT_MAX - 2)
                                string   szInfo;
                                MqlTick  tick;
                                MqlRates rate;
                                int      i0;
                                
                                Print("Carregando ticks de replay. Aguarde...");
                                ArrayResize(m_Ticks.Info, def_MaxSizeArray, def_MaxSizeArray);
                                i0 = m_Ticks.nTicks;
                                while ((!FileIsEnding(m_File)) && (m_Ticks.nTicks < def_LIMIT) && (!_StopFlag))
                                {
                                        ArrayResize(m_Ticks.Info, m_Ticks.nTicks + 1, def_MaxSizeArray);
                                        szInfo = FileReadString(m_File) + " " + FileReadString(m_File);
                                        tick.time = StringToTime(StringSubstr(szInfo, 0, 19));
                                        tick.time_msc = (int)StringToInteger(StringSubstr(szInfo, 20, 3));
                                        tick.bid = StringToDouble(FileReadString(m_File));
                                        tick.ask = StringToDouble(FileReadString(m_File));
                                        tick.last = StringToDouble(FileReadString(m_File));
                                        tick.volume_real = StringToDouble(FileReadString(m_File));
                                        tick.flags = (uchar)StringToInteger(FileReadString(m_File));
                                        if ((m_Ticks.Info[i0].last == tick.last) && (m_Ticks.Info[i0].time == tick.time) && (m_Ticks.Info[i0].time_msc == tick.time_msc))
                                                m_Ticks.Info[i0].volume_real += tick.volume_real;
                                        else
                                        {
                                                m_Ticks.Info[m_Ticks.nTicks] = tick;
                                                if (tick.volume_real > 0.0)
                                                {
                                                        ArrayResize(m_Ticks.Rate, (m_Ticks.nRate > 0 ? m_Ticks.nRate + 2 : def_BarsDiary), def_BarsDiary);
                                                        m_Ticks.nRate += (BuiderBar1Min(rate, tick) ? 1 : 0);
                                                        m_Ticks.Rate[m_Ticks.nRate] = rate;
                                                        m_Ticks.nTicks++;
                                                }
                                                i0 = (m_Ticks.nTicks > 0 ? m_Ticks.nTicks - 1 : i0);
                                        }
                                }
                                FileClose(m_File);
                                if (m_Ticks.nTicks == def_LIMIT)
                                {
                                        Print("Excesso de dados no arquivo de tick.\nNão é possivel continuar...");
                                        return false;
                                }
                                return (!_StopFlag);
#undef def_LIMIT
                        }

mas notem que algumas partes, foram riscadas. Sendo assim removidas do código. Então o código final ficou como mostrado logo abaixo:

inline bool ReadAllsTicks(const bool ToReplay)
                        {
#define def_LIMIT (INT_MAX - 2)
#define def_Ticks m_Ticks.Info[m_Ticks.nTicks]

                                string   szInfo;
                                MqlRates rate;
                                
                                Print("Carregando ticks de replay. Aguarde...");
                                ArrayResize(m_Ticks.Info, def_MaxSizeArray, def_MaxSizeArray);
                                while ((!FileIsEnding(m_File)) && (m_Ticks.nTicks < def_LIMIT) && (!_StopFlag))
                                {
                                        ArrayResize(m_Ticks.Info, m_Ticks.nTicks + 1, def_MaxSizeArray);
                                        szInfo = FileReadString(m_File) + " " + FileReadString(m_File);
                                        def_Ticks.time = StringToTime(StringSubstr(szInfo, 0, 19));
                                        def_Ticks.time_msc = (int)StringToInteger(StringSubstr(szInfo, 20, 3));
                                        def_Ticks.bid = StringToDouble(FileReadString(m_File));
                                        def_Ticks.ask = StringToDouble(FileReadString(m_File));
                                        def_Ticks.last = StringToDouble(FileReadString(m_File));
                                        def_Ticks.volume_real = StringToDouble(FileReadString(m_File));
                                        def_Ticks.flags = (uchar)StringToInteger(FileReadString(m_File));
                                        if (def_Ticks.volume_real > 0.0)
                                        {
                                                ArrayResize(m_Ticks.Rate, (m_Ticks.nRate > 0 ? m_Ticks.nRate + 2 : def_BarsDiary), def_BarsDiary);
                                                m_Ticks.nRate += (BuiderBar1Min(rate, def_Ticks) ? 1 : 0);
                                                m_Ticks.Rate[m_Ticks.nRate] = rate;
                                        }
                                        m_Ticks.nTicks++;
                                }
                                FileClose(m_File);
                                if (m_Ticks.nTicks == def_LIMIT)
                                {
                                        Print("Excesso de dados no arquivo de tick.\nNão é possivel continuar...");
                                        return false;
                                }
                                return (!_StopFlag);
#undef def_Ticks
#undef def_LIMIT
                        }

Esta é a nova rotina de leitura dos tickets reais, notem que ela agora, não mais ignora os valores contidos nas posições BID e ASK. E não mais, acumula também, os valores, caso a posição também permita. Ou seja, ela esta lendo os dados integramente e os colocando na memória. Por conta que as mudanças, não geraram nenhum novo procedimento dentro da rotina. Muito pelo contrário, elas simplificam a mesma. Acredito que você, que esta acompanhando este serie de artigos, não terá dificuldades em entender, o que realmente esta acontecendo. Mas o fato de termos feito estas simplificações, tem consequências em outros pontos do código. Alguns destes pontos, irão sofrer bastante. Por isto, foi preciso desabilitar algumas coisas, até que todo o código realmente volte a funcionar de forma estável.

Poderíamos fazer as mudanças, estabilizar o código e mostrar logo a versão final. Mas acredito que mostrando, o passo a passo de como as mudanças, serão feitas. Será algo de grande valia, para quem esta aprendendo e deseja realmente entender, como as coisas funcionam nos seus pormenores. Além disto existe um outro motivo, para explicar tais mudanças. Mas principalmente, fazendo as coisas com calma e sem grandes sobressaltos. Tornará mais acessível o aprendizado de detalhes, que muitas vezes são mal compreendidos. E pior, muitos destes detalhes são mal explicados por quem diz ser um operador profissional. Isto considerando aqueles que dizem realmente viver de operar no mercado financeiro. Mas tais questões, estão além do escopo desta sequência de artigos. Não vamos nos desviar do nosso foco principal. Então vamos continuar a implementar as coisas aos poucos. Pois assim tudo passará a fazer mais sentido depois. Principalmente quando formos falar de um outro mercado também bastante curioso e interessante. Mas não vou estragar a surpresa. Continue acompanhando os artigos e você irá ver do que estou falando.

Uma vez feitas estas primeiras mudanças. Precisaremos fazer uma outra alteração. Esta um pouco mais estranha, mas igualmente necessária. Já que agora, teremos valores, onde não existe nenhum volume ( valores de BID e ASK ). Precisamos fazer com que o sistema inicie, em um ponto, onde temos algum volume indicado.

class C_ConfigService : protected C_FileTicks
{
        protected:
//+------------------------------------------------------------------+
                datetime m_dtPrevLoading;
                int      m_ReplayCount;
//+------------------------------------------------------------------+
inline void FirstBarNULL(void)
                        {
                                MqlRates rate[1];
                                
                                for(int c0 = 0; m_Ticks.Info[c0].volume_real == 0; c0++)
                                        rate[0].close = m_Ticks.Info[c0].last;
                                rate[0].open = rate[0].high = rate[0].low = rate[0].close;
                                rate[0].tick_volume = 0;
                                rate[0].real_volume = 0;
                                rate[0].time = m_Ticks.Info[0].time - 60;
                                CustomRatesUpdate(def_SymbolReplay, rate);
                                m_ReplayCount = 0;
                        }
//+------------------------------------------------------------------+

//... Restante da classe ...

}

Esta rotina, originalmente estava sendo privativa da classe, e não contava com os pontos em destaque. Mas juntamente com ela, que agora é uma rotina protegida, temos também uma variável. Esta por um acaso, é justamente a usada no contador do replay. Esta variável, esta aqui, apenas para ter o seu valor modificado, por esta rotina especifica. E somente por ela. Este laço, irá fazer com que aquela barra inicial, que fica na extrema esquerda do gráfico, tenha um valor adequado. Lembre-se: Agora temos os valores BID e ASK, junto com os valores de preço. No entanto, os valores de BID e ASK, não tem nenhum significado para nos, pelo menos não neste momento.

Bem, até aqui é tudo bastante simples e fácil de ser compreendido. Agora iremos entrar na classe responsável pelo replay. E esta parte, contém algumas coisas bastante estranhas, e que não fazem muito sentido a primeira vista. Então vamos ver isto no próximo tópico.


Modificando a Classe C_Replay

As mudanças aqui, começam de uma forma mais simples, e vão se tornando bastante estranhas. Vamos começar com a parte fácil das mudanças, esta pode ser vista logo abaixo:

                void AdjustPositionToReplay(const bool bViewBuider)
                        {
                                u_Interprocess Info;
                                MqlRates       Rate[def_BarsDiary];
                                int            iPos,   nCount;
                                
                                Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
                                if (m_ReplayCount == 0)
                                        for (; m_Ticks.Info[m_ReplayCount].volume_real == 0; m_ReplayCount++);
                                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)) FirstBarNULL(); 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);
                        }

Esta rotina, ainda não esta realmente finalizada. Por conta disto, é que tivemos, de bloquear a visualização do sistema, de construção das barras de 1 minuto. Mas mesmo sem estar de fato finalizada, foi necessário adicionar um código extra, na mesma. Este código faz algo muito parecido, com o que acontece ao colocarmos a barra, na extrema esquerda do gráfico. Muito provavelmente, um dos códigos irá desaparecer em futuras versões. Mas este daqui, além daquele trabalho, faz algo ainda mais sutil. Ele de fato evita que ao iniciar o replay / simulação, o ativo dê um pulo, antes que a primeira barra seja de fato plotada. Se você desabilitar esta linha de código, irá ver que no inicio da plotagem, acontecerá um pulo. Este pulo se deve, a um outro fato, que é a próxima coisa a ser vista.

Para poder explicar como de fato foi possível. E é possível, adicionar os tickets na janela de observação de mercado. É preciso ver a rotina original de criação das barras. Esta pode ser vista abaixo:

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
                        }

Esta rotina original, é responsável apenas e somente, pela criação das barras, que são plotadas no gráfico. Quero que vocês, prestem bastante atenção ao código acima, e comparem ele com o código abaixo:

inline void CreateBarInReplay(const bool bViewMetrics = false)
                        {
#define def_Rate m_MountBar.Rate[0]

                                bool bNew;

                                if (m_MountBar.memDT != macroRemoveSec(m_Ticks.Info[m_ReplayCount].time))
                                {                               
                                        if (bViewMetrics) Metrics();
                                        m_MountBar.memDT = macroRemoveSec(m_Ticks.Info[m_ReplayCount].time);
                                        def_Rate.real_volume = 0;
                                        def_Rate.tick_volume = 0;
                                }
                                bNew = (def_Rate.tick_volume == 0);
                                def_Rate.close = (m_Ticks.Info[m_ReplayCount].volume_real > 0.0 ? m_Ticks.Info[m_ReplayCount].last : def_Rate.close);
                                def_Rate.open = (bNew ? def_Rate.close : def_Rate.open);
                                def_Rate.high = (bNew || (def_Rate.close > def_Rate.high) ? def_Rate.close : def_Rate.high);
                                def_Rate.low = (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;
                                CustomRatesUpdate(def_SymbolReplay, m_MountBar.Rate);
                                ViewTick();
                                m_ReplayCount++;
                                
#undef def_Rate
                        }

Parece ser a mesma coisa, mas não. Não é a mesma coisa, existem diferenças. E não é, no fato de que este segundo código, conta com duas novas chamadas. Sendo que na verdade, a primeira chamada, está ai apenas por que decidi remover o código de métricas de dentro da rotina. O código de métricas, pode ser visto abaixo. Ele é exatamente o que estava na rotina original.

inline void Metrics(void)
                        {
                                int i;
                                static ulong _mdt = 0;
                                
                                _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();
                                
                        }

No bem da verdade, a maior diferença esta no como o sistema encontra o preço de fechamento da barra. Quando não havia interferência dos valores de BID e ASK, era bastante simples saber qual era o valor, a ser usado como sendo o preço de fechamento. Mas com o BID e ASK, interferindo na cadeia de dados, precisamos de uma outra forma para fazer isto. E é justamente olhando, se a posição tem, ou não algum volume de negócio, é que ficamos sabendo, se é, ou não, um valor que pode ser utilizado, como sendo o preço de fechamento.

Esta é a grande questão nesta nova rotina. Além disto, ainda contamos com duas chamas: A primeiro já mostrei. Agora a segunda, é onde a coisa realmente se torna bastante estranha.

O código desta segunda chamada pode ser visto logo abaixo:

inline void ViewTick(void)
                        {
                                MqlTick tick[1];

                                tick[0] = m_Ticks.Info[m_ReplayCount];
                                tick[0].time_msc = (m_Ticks.Info[m_ReplayCount].time * 1000) + m_Ticks.Info[m_ReplayCount].time_msc;
                                CustomTicksAdd(def_SymbolReplay, tick);
                        }

Este código pode parecer absolutamente bizarro, mas no entanto funciona. O motivo disto, pode ser visto na documentação sobre a forma de usar a função CustomTicksAdd. Não quero aqui gerar polêmica. Então vou usar exatamente o conteúdo que está presente na documentação, antes de explicar por que esta rotina acima funciona. E por que ela tem que ser exatamente assim.

Segue o conteúdo da documentação:

Nota adicional

A função CustomTicksAdd só funciona para símbolos personalizados abertos na janela de Market Watch. Se o símbolo não estiver selecionado no Market Watch, você deve adicionar ticks usando CustomTicksReplace.

A função CustomTicksAdd permite transmitir ticks como se chegassem desde o servidor da corretora. Os dados não são salvos diretamente no banco de dados de ticks, em vez disso, são enviados para a janela "Observação do mercado". É a partir dela que o terminal armazena os ticks em sua base. Quando a quantidade de dados transferidos por chamada é grande, a função altera seu comportamento para poupar recursos. Se forem transferidos mais de 256 ticks, os dados serão divididos em duas partes. A primeira parte (grande) é imediatamente carregada diretamente na base de ticks (como faz a CustomTicksReplace). A segunda parte, que consiste nos últimos 128 ticks, é transferida para a janela "Observação do mercado" e, em seguida, é salva pelo terminal no banco de dados de ticks.

A estrutura MqlTick possui dois valores de campo com valores temporais, isto é, time (hora do tick em segundos) e time_msc (hora de tick em milissegundos). Eles realizam o cálculo desde 01 de janeiro de 1970. Nos ticks adicionados, o processamento destes campos é realizado acordo com as seguintes regras na ordem estabelecida:

  1. se o valor for ticks[k].time_msc!=0, usamo-lo para preenchimento do campo ticks[k].time, quer dizer, para o tick é definido o tempo ticks[k].time=ticks[k].time_msc/1000 (divisão inteira)
  2. se ticks[k].time_msc==0 e ticks[k].time!=0, a hora em milissegundos é obtida pela multiplicação por 1000, isto é, ticks[k].time_msc=ticks[k].time*1000
  3. se ticks[k].time_msc==0 e ticks[k].time==0, nestes campos é registrado o tempo atual do servidor de negociação em milissegundos quando chamada a funçãoCustomTicksAdd.

Se o valor dos campos ticks[k].bid, ticks[k].ask, ticks[k].last ou ticks[k].volume for maior do que zero, no campo ticks[k].flags, é escrita a combinação dos sinalizadores correspondentes:

  • TICK_FLAG_BID — tick alterou o preço Bid
  • TICK_FLAG_ASK  — tick alterou o preço Ask
  • TICK_FLAG_LAST — tick alterou o preço da última transação
  • TICK_FLAG_VOLUME — tick mudou o volume

Se o valor de campo é menor ou igual a zero, o sinalizador correspondente não é gravado nos campo ticks[k].flags. 

Sinalizadores TICK_FLAG_BUY e TICK_FLAG_SELL não são adicionados no histórico do instrumento personalizado.

O detalhe desta nota, é que para muita gente, ela não faz muito sentido. Mas é justamente o que ela informa, é que estou usando para fazer as coisas funcionarem. Aqui é especificado as condições onde: O tempo em milissegundos diferente de zero; O tempo em milissegundos igual a zero e o tempo de ticket é diferente de zero; E quando tanto o tempo em milissegundos quanto o tempo do ticket são iguais a zero. O grande problema, é que ao usarmos tickets reais de um arquivo. Estas condições, não são tão claras, para a grande maioria. E isto faz com que tenhamos um problema. Se a pessoa tentar utilizar os tickets reais, obtidos em um arquivo, de forma a conseguir jogar estes dados, nas informações de tickets. Não irá conseguir obter o resultado desejado.

Por conta disto, que muitos podem até estar tentando fazer tal modelagem, mas não estão conseguindo. Pelo simples fato, de que eles não estão entendendo a documentação. Mas utilizando justamente este fato ( que esta implícito na documentação ), é que montei o código visto acima. Nele estou forçando a primeira das condições. Ou seja, onde o valor do tempo em milissegundos, é diferente de zero. Mas você deve se atendar, ao fato de que o valor, que indica o tempo em milissegundos, deve conter também, o valor do tempo. Pois será feito um calculo, pelo MetaTrader 5, a fim de gerar o valor, do tempo. E é por conta disto, que precisamos ajustar as coisas, no valor informado no campo milissegundos.

Desta forma a função CustomTicksAdd, irá conseguir jogar os dados na observação de mercado. Mas não é somente isto, ao jogar tais dados no sistema, irá também aparecer as linhas de preço de BID, preço ASK e a linha do ultimo preço no gráfico que esta sendo plotado. Ou seja, de bônus por termos conseguido jogar os tickets, na observação de mercado, também ganhamos as linhas de preço no gráfico. Algo que não estava presente, por falta justamente deste tipo de coisa. Mas não comemore ainda, pois o sistema não esta concluído. Ainda existem algumas coisas, que precisam ser vista, corrigidas e montadas. Mas por conta disto, é que estou utilizando e disponibilizando, dados de TICKETS REAIS, para que vocês possam experimentar, esta nova fase do sistema de replay / simulador.


Ultimas considerações

Este artigo irá terminar neste ponto. Já que as coisas que precisam ser ainda feitas, irão gerar muita confusão na explicação que já foi dada. Desta maneira, no próximo artigo, irei mostrar como corrigir algumas coisas, que não estão funcionando de forma adequada, neste sistema atual. Apesar de tudo, você poderá utilizar o sistema, sem de fato fazer o avanço ou retrocesso rápido. Se isto for feito, pode acontecer de os dados dos tickets na observação de mercado, ou as informações de linha dos preços, não baterem com a atual situação vista no gráfico do replay / simulação.

Para não dizer que fico dando privilégios, apenas ao uso de contratos como mini índice. Quero que você experimente este sistema em outros ativos. Assim ficará mais claro, como o sistema de replay / simulador estará se comportando frente ao que estamos implementando nele. Só quero deixar claro o seguinte fato. Existe ainda algumas falhas presentes no sistema de deslocamento rápido. Desta forma, tenha um pouco paciência com relação a isto. Minha sugestão é que você evite, pelo menos por hora, fazer uso do avanço rápido.

Nestes testes que sugiro, você fazer, quero que você preste a devida atenção, tanto na liquidez, quanto em termos de volatilidade do ativo que você escolher. Observe as métricas em mais de um ativo diferente. Note que em ativos com um numero menor de negócios, sendo feitos no intervalo de 1 minuto. Acaba deixando o sistema de replay, meio que sem saber como trabalhar com tais ativos. De certa forma, é bom ver isto neste momento, pois isto precisa ser corrigido. Apenas de que a montagem das barras aparentemente esta correta. E faremos esta correção em breve. Mas quero que você, caro leitor, entenda por que que o serviço de replay / simulador fica estranho. Isto antes de corrigirmos esta falha. Tal entendimento, é importante caso você queira de fato se aprofundar na programação. Não fique sempre naquela de programas simples e fáceis de serem criados. Os verdadeiros programadores, são aqueles que resolvem problemas quando eles surgem. E não aqueles que desistem no primeiro sinal de dificuldade.

No entanto, ao observar o tempo, tanto na janela de observação de mercado, quanto no valor informado pelo sistema de métricas, é que o serviço de replay / simulador, não esta conseguindo temporizar de forma adequada o sistema. Isto quando os tempos, são maiores que 1 segundo. Precisamos e vamos vamos corrigir isto em breve. Mas até lá, estudem este código, pois irá de fato valer bastante em termos de aprendizagem e de como trabalhar com tickets na janela de observação de mercado. Então nos vemos no próximo artigo. Pois a coisa ainda irá ficar mais empolgante.


Arquivos anexados |
Indicadores baseados na classe CCanvas: Preenchendo canais com transparência Indicadores baseados na classe CCanvas: Preenchendo canais com transparência
Neste artigo, abordaremos os métodos de criação de indicadores personalizados que são desenhados usando a classe CCanvas da Biblioteca Padrão no MetaTrader 5. Também discutiremos as propriedades dos gráficos para a transformação de coordenadas. Daremos especial atenção aos indicadores que preenchem a área entre duas linhas usando transparência.
Desenvolvimento de um sistema de negociação baseado no indicador Fibonacci Desenvolvimento de um sistema de negociação baseado no indicador Fibonacci
Esta é a continuação de uma série de artigos nos quais aprendemos como construir sistemas de negociação com base nos indicadores mais populares. Desta vez, cobriremos o indicador Fibonacci. Veremos como escrever um programa baseado nos sinais deste indicador.
Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 18):  Tiquete e mais tiquetes  (II) Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 18): Tiquete e mais tiquetes (II)
Neste, fica extremamente claro, que as métricas, estão muito longe, do tempo ideal de confecção das barras de 1 minuto. Assim então, a primeira coisa que de fato iremos corrigir, será justamente isto. Corrigir a questão da temporização, não é algo complicado. Por mais incrível que possa parecer, é na verdade até bem simples de ser feito. Porém não fiz a correção no artigo anterior, por que lá o desejo era explicar, como fazer para jogar os dados de tickets, que estavam sendo usados para gerar as barras de 1 minuto no gráfico, para dentro da janela de observação de mercado.
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.