English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
preview
Desenvolvendo um EA de negociação do zero (Parte 14): Volume at Price (II)

Desenvolvendo um EA de negociação do zero (Parte 14): Volume at Price (II)

MetaTrader 5Sistemas de negociação | 10 maio 2022, 11:46
1 631 0
Daniel Jose
Daniel Jose

1.0 - Introdução

Nosso EA já conta com diversos recursos para nos ajudar durante a negociação, estes recursos foram sendo adicionados no decorrer dos artigos até chegarmos neste daqui, mas apesar de tudo ele conta com pequenos problemas na questão de visualização e redimensionamento. Não são coisas que de fato atrapalham na negociação, mas que em alguns momentos poluem a tela até que forcemos uma atualização da mesma, bem além disto nos falta algumas coisas que seriam informações que pode nos valer muito, mas são coisas especificas, porém pode ser necessárias.

Então mão a obra e vamos começar a implementar estas novas melhorias, este artigo vai ser bastante interessante, podendo direcionar você a novas ideias e métodos de apresentar informações e ao mesmo tempo corrigir pequenas falhas nos seus projetos.


2.0 - Planejando e implementando um novo recurso no Volume At Price

2.1 - Planejamento

Bem, existe uma coisa curiosa sobre negociação, muitas vezes vemos o mercado acumular em certas regiões de preço, e quando ocorre o acionamento de stops seja do lado comprado, seja do lado vendido, existe uma rápida movimentação do preço, e isto pode ser visto no Times & Trade, que apresentei nos artigos Times & Trade ( I ) e Times & Trade ( II ), onde mostrei como criar um sistema gráfico alternativo para leitura e analise do fluxo de ordens executadas, pois bem, se você observar irá notar que em alguns destes momentos o preço tende a voltar para a região de acumulação, de onde ele não queria sair naquele momento, mas quando você olha o indicador de Volume at Price fica difícil identificar o qual recente foi a permanência do preço naquela região especifica, o indicador em questão foi implementado no artigo Adicionando o Volume at Price ( I ), naquele indicador temos a possibilidade de analisar movimentos mais ou menos recentes, bastando modificar o ponto de inicio da analise, que é feito ao se ajustar o valor do objeto que esta sendo indicado na figura abaixo:

Mas isto não é de fato prático, já que ficamos presos ao tempo gráfico principal, ou seja, se você estiver com o gráfico em um timeframe de 60 minutos, não conseguirá analisar movimentos abaixo deste tempo, terá que mudar para um tempo inferior para poder ajustar o ponto de analise, mas no caso de operar contratos futuros, normalmente grande parte dos operadores de fato fazem isto em tempos mais baixos como 5, 10 ou até mesmo 30 minutos, então não parece problema ajustar o ponto de analise, mas como eu expliquei acima, as vezes o preço sai de um região de acumulação não por que ele assim desejou, mas por ter sido acionado stops de posições, e este retorno normalmente se dá em temos inferiores a 5 minutos, e no gráfico você verá um candlestick com uma longa sombra seja superior ou inferior, e nestes casos o Price Action diz que o que houve foi uma sondagem de mercado, este tipo de movimento pode ser visto abaixo nos candles indicados pelas setas:

    Típico movimento de sondagem dos compradores, ou acionamento de stop dos vendidos

  Típico momento de sondagem dos vendedores, ou acionamento de stop dos comprados.

Este tipo de movimento costuma acontecer muitas vezes, e analisar o volume que aconteceu em cada uma das faixas de preço é muito importante para saber se o mercado esta sondando ou se de fato esta virando a tendência em uma direção, e isto não é possível ser feito de forma adequada, ou melhor dizendo rapidamente pelo indicador de volume proposto anteriormente.

Mas podemos fazer uma pequena modificação na classe objeto do indicador e assim termos uma indicação mais clara do que esta acontecendo, e isto irá aparecer como sendo um rastro da negociação que aconteceu em um dado período de tempo.


2.2 Implementando

Bom a primeira coisa a ser feita, é analisar quanto tempo você deseja de fato ter um rastro, se serão 60, 45, 30, 19, 7 ou 1 minuto, mas independente disto aconselho a usar valores sejam bastante divisíveis, isto para que o sistema de rastro de fato possa ser útil, bem por motivos práticos irei implementar ele usando 30 minutos de rastro, então definimos isto na seguinte linha de código:

#define def_MaxTrailMinutes     30

E por que de 30 minutos ?!?! Bem na verdade, o sistema de rastro será feito de 1 em 1 minuto, mas o tempo máximo que teremos de rastro será de 30 minutos, ou seja, o rastro no volume sempre será de 30 minutos, quando se passarem 31 minutos, o primeiro minuto de negociação já não terá seu rastro mostrado. E como isto é conseguido ?!?! Isto é feito pelo sistema de captura que é mostrado logo abaixo:

inline void SetMatrix(MqlTick &tick)
{
        int pos;
                                
        if ((tick.last == 0) || ((tick.flags & (TICK_FLAG_BUY | TICK_FLAG_SELL)) == (TICK_FLAG_BUY | TICK_FLAG_SELL))) return;
        pos = (int) ((tick.last - m_Infos.FirstPrice) / Terminal.GetPointPerTick()) * 2;
        pos = (pos >= 0 ? pos : (pos * -1) - 1);
        if ((tick.flags & TICK_FLAG_BUY) == TICK_FLAG_BUY) m_InfoAllVaP[pos].nVolBuy += tick.volume; else
        if ((tick.flags & TICK_FLAG_SELL) == TICK_FLAG_SELL) m_InfoAllVaP[pos].nVolSell += tick.volume;
        m_InfoAllVaP[pos].nVolDif = (long)(m_InfoAllVaP[pos].nVolBuy - m_InfoAllVaP[pos].nVolSell);
        m_InfoAllVaP[pos].nVolTotal = m_InfoAllVaP[pos].nVolBuy + m_InfoAllVaP[pos].nVolSell;
        m_Infos.MaxVolume = (m_Infos.MaxVolume > m_InfoAllVaP[pos].nVolTotal ? m_Infos.MaxVolume : m_InfoAllVaP[pos].nVolTotal);
        m_Infos.CountInfos = (m_Infos.CountInfos == 0 ? 1 : (m_Infos.CountInfos > pos ? m_Infos.CountInfos : pos));
        m_Infos.Momentum = macroGetMin(tick.time);
        m_Infos.Momentum = (m_Infos.Momentum > (def_MaxTrailMinutes - 1) ? m_Infos.Momentum - def_MaxTrailMinutes : m_Infos.Momentum);
        if (m_Infos.memMomentum != m_Infos.Momentum)
        {
                for (int c0 = 0; c0 <= m_Infos.CountInfos; c0++) m_TrailG30[m_Infos.Momentum].nVolume[c0] = 0;
                m_Infos.memMomentum = m_Infos.Momentum;
        }
        m_TrailG30[m_Infos.Momentum].nVolume[pos] += tick.volume;
}

As linhas em destaque foram adicionadas ao código original da classe objeto, com isto temos a captura do rastro de volume, e as linhas no fragmento abaixo, garantem que o rastro será feito da forma esperada.

m_Infos.Momentum = macroGetMin(tick.time);
m_Infos.Momentum = (m_Infos.Momentum > (def_MaxTrailMinutes - 1) ? m_Infos.Momentum - def_MaxTrailMinutes : m_Infos.Momentum);

Bom, já temos o sistema de captura de rastro criado, agora temos que tomar uma nova decisão, lembre-se que o rastro é capturado de 1 em 1 minuto, ou seja podemos apresentar ele de forma a ver o volume a cada faixa de preço no período de 1 minuto, desde que façamos a plotagem desta forma, você poderia pensar em fazer algo como mostrado abaixo:

  Tons mais claros representam volumes mais recentes, pode ser uma boa ideia ....

Apesar de parecer ser uma boa ideia, quando o volume for baixo, ou o movimento for muito rápido, mesmo com um volume expressivo para o momento, pode ser que de fato ele não venha a ser visível, já que ele será plotado se ajustando ao volume máximo que foi executado até aquele momento, então você pode decidir fazer uma plotagem um pouco diferente para resolver isto, então ficaria conforme mostrado abaixo:

Cada cor representa um determinado período, no rastro do volume....

Isto pode ajudar a você analisar faixas muito estreitas no volume, corrigindo em alguns momentos o problema que é visto no primeiro caso, mas ainda assim teríamos o problema de ajuste que ocorre quando o volume pode não ser tão expressivo frente ao volume total em um outro ponto, além de você ter que escolher muito bem as cores para cada período de forma que não fique confuso analisar nos momentos de forte negociação.

Então, aqui irei usar um modelo mais singelo, que novamente você pode ajustar para analisar movimentos de períodos diferentes conforme mostrei acima, isto sem nenhum problema, mas tenha em mente os problemas que também deixei claro que irão acontecer, mas isto fica ao seu critério, então a imagem do rastro ficará conforme mostrado abaixo:

Aqui temos o rastro singelo, nele devemos observar tanto o Times & Trade quanto o Price Action para de fato ter uma noção do que esta acontecendo....

Bem, de uma forma ou de outra a única função que deverá ser modificada para se mudar a apresentação do volume é mostrada abaixo:

void Redraw(void)
{
        uint            x, y, y1, p;
        double  reason = (double) (m_Infos.MaxVolume > m_WidthMax ? (m_WidthMax / (m_Infos.MaxVolume * 1.0)) : 1.0);
        double  desl = Terminal.GetPointPerTick() / 2.0;
        ulong           uValue;
                                
        Erase();
        p = m_WidthMax - 8;
        for (int c0 = 0; c0 <= m_Infos.CountInfos; c0++)
        {
                if (m_InfoAllVaP[c0].nVolTotal == 0) continue;
                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, m_Infos.FirstPrice + (Terminal.GetPointPerTick() * (((c0 & 1) == 1 ? -(c0 + 1) : c0) / 2)) + desl, x, y);
                y1 = y + Terminal.GetHeightBar();
                FillRectangle(p + 2, y, p + 8, y1, macroColorRGBA(m_InfoAllVaP[c0].nVolDif > 0 ? m_Infos.ColorBuy : m_Infos.ColorSell, m_Infos.Transparency));
                FillRectangle((int)(p - (m_InfoAllVaP[c0].nVolTotal * reason)), y, p, y1, macroColorRGBA(m_Infos.ColorBars, m_Infos.Transparency));
                uValue = 0;
                for (int c1 = 0; c1 < def_MaxTrailMinutes; c1++) uValue += m_TrailG30[c1].nVolume[c0];
                FillRectangle((int) (p - (uValue * reason)), y, p, y1, macroColorRGBA(clrRoyalBlue, m_Infos.Transparency));
        }
        C_Canvas::Update();
};

E para ser mais exato, apenas o código em destaque precisa de fato ser modificado, você pode brincar a vontade com isto até conseguir o resultado desejado, nada mais na classe precisa ser modificada além deste trecho em destaque acima, e ao compilar e executar o EA no gráfico você terá algo parecido com a imagem abaixo:



3.0 Corrigindo o problema de Render

Apesar do código não ter de fato problemas, existe uma falha que quando você muda o gráfico de um tamanho, de maximizado, para um outro tamanho qualquer, e depois volta ele para maximizado, alguns objetos ficam meio que perdidos e podem não ter o comportamento esperado, sendo posicionados em locais errados, então para corrigir isto não adianta ficar coçando a cabeça e se desesperando, mas devemos modificar o seguinte código do EA visto abaixo, este é o código original que pode ser visto nos artigos anteriores:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        ChartRedraw();
}

A modificação é simples, mas você pode pensar, não estou vendo nada de errado ai, o código está correto !?!?! A primeira vista eu também não vejo nada de errado, e o código permanecia com uma falha em tempo de execução, mas quando foi adicionar algumas funcionalidades extras notei o problema, que é justamente o descrito acima, e para resolver temos que mudar o código acima para o código abaixo:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        ChartRedraw();
}

Parece algo bobo, mas para entender é preciso olhar o código da função que esta em destaque, mas agora que o sistema foi corrigido podemos passar para a nova etapa.


4.0 Adicionando recursos extras

O recurso que vamos adicionar agora é algo de certa forma singelo, muitos talvez não vão ver um grande motivo para que ele seja implementado, mas sua implementação irá ajudar em muito no momento de trabalhar com as ordens, seja posicionando, movendo ou simplesmente observando o indicador de volume at price.

A primeira coisa a se fazer é trocar a classe na qual o código de ajuste da linha do preço irá fazer parte, este código sai da classe C_OrderView e vai para a classe C_Terminal, mas ele também sofre com isto pequenas modificações, já que irá passar a tratar com as variáveis da própria classe, o fragmento abaixo mostra como o novo código irá ficar.

double AdjustPrice(const double arg)
{
        double v0, v1;
                                
        if(m_Infos.TypeSymbol == OTHER) return arg;
        v0 = (m_Infos.TypeSymbol == WDO ? round(arg * 10.0) : round(arg));
        v1 = fmod(round(v0), 5.0);
        v0 -= ((v1 != 0) || (v1 != 5) ? v1 : 0);
        return (m_Infos.TypeSymbol == WDO ? v0 / 10.0 : v0);
};

Feito isto podemos criar a nova classe do EA, esta será a classe C_Mouse, em suma esta classe objeto irá cuida e será a base dos eventos envolvidos com o mouse, então vamos ver como ela se encontra neste momento do desenvolvimento. Mas antes vamos ver a atual estrutura de classes do nosso EA, isto é mostrado na imagem abaixo:

 Uma nova estrutura se faz necessária para o proximo sistema que será implementado....

Bem, visto a estrutura acima, vamos então dissecar o código da classe objeto C_Mouse começando com a declaração das variáveis que pode ser visto abaixo:

class C_Mouse
{
        private  :
                struct st00
                {
                color   cor01,
                        cor02,
                        cor03;
                string  szNameObjH,
                        szNameObjV,
                        szNameObjT,
                        szNameObjI,
                        szNameObjB;
                }m_Infos;
                struct st01
                {
                        int      X,
                                 Y;
                        datetime dt;
                        double   price;
                        uint     ButtonsStatus;
                }Position;

Vejam que são poucas as variáveis necessárias neste atual estágio de desenvolvimento, o próximo ponto que merece a nossa atenção é visto no fragmento abaixo:

~C_Mouse()
{
// ... Código interno ...
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_MOUSE_MOVE, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_CROSSHAIR_TOOL, true);
}

Este código restabelece tanto o CHART_CROSSHAIR_TOOL quanto desativa o uso de eventos de mouse pelo gráfico, ou seja o MT5 não irá mais se preocupar em enviar eventos deste tipo para o gráfico, eles serão tratados pela própria plataforma.

Temos também duas rotinas muito comuns quando temos a programação em que iremos controlar o mouse, elas podem ser vista logo abaixo:

inline void Show(void)
{
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjH, OBJPROP_COLOR, m_Infos.cor01);
}
//+------------------------------------------------------------------+
inline void Hide(void)
{
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjH, OBJPROP_COLOR, clrNONE);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_COLOR, clrNONE);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, clrNONE);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_COLOR, clrNONE);
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjB, 0, 0, 0);
}

Uma coisa interessante é que de fato o mouse não desaparece da tela, apenas os objetos que criamos irão desaparecer da tela, e quando "ligarmos" o mouse apenas a linha de preço ficará de fato visível, isto pode parecer curioso, mas tem suas utilidades em alguns pontos específicos do EA, um destes pontos é na classe objeto C_OrderView no trecho mostrado em destaque no código abaixo:

inline void MoveTo(uint Key)
{
        static double local = 0;
        int w = 0;
        datetime dt;
        bool bEClick, bKeyBuy, bKeySell;
        double take = 0, stop = 0, price;
                                
        bEClick  = (Key & 0x01) == 0x01;    //Clique esquerdo
        bKeyBuy  = (Key & 0x04) == 0x04;    //SHIFT Pressionada
        bKeySell = (Key & 0x08) == 0x08;    //CTRL Pressionada
        Mouse.GetPositionDP(dt, price);
        if (bKeyBuy != bKeySell) Mouse.Hide(); else Mouse.Show();
        ObjectMove(Terminal.Get_ID(), m_Infos.szHLinePrice, 0, 0, price = (bKeyBuy != bKeySell ? price : 0));
        ObjectMove(Terminal.Get_ID(), m_Infos.szHLineTake, 0, 0, take = price + (m_Infos.TakeProfit * (bKeyBuy ? 1 : -1)));
        ObjectMove(Terminal.Get_ID(), m_Infos.szHLineStop, 0, 0, stop = price + (m_Infos.StopLoss * (bKeyBuy ? -1 : 1)));
        if((bEClick) && (bKeyBuy != bKeySell) && (local == 0)) CreateOrderPendent(bKeyBuy, m_Infos.Volume, local = price, take, stop, m_Infos.IsDayTrade); else local = 0;
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLinePrice, OBJPROP_COLOR, (bKeyBuy != bKeySell ? m_Infos.cPrice : clrNONE));
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLineTake, OBJPROP_COLOR, (take > 0 ? m_Infos.cTake : clrNONE));
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLineStop, OBJPROP_COLOR, (stop > 0 ? m_Infos.cStop : clrNONE));
};

Mas reparem na linha imediatamente acima da linha em destaque:

Mouse.GetPositionDP(dt, price);

Esta linha irá capturar os valores de posição do mouse, e o código que irá reportar estes valores é visto abaixo:

inline void GetPositionDP(datetime &dt, double &price)
{
        dt = Position.dt;
        price = Position.price;
}

mas não é somente isto, pode ser que em alguns casos precisamos das coordenadas cartesianas do gráfico, em termos de posição de tela, e para conseguir estes valores uma outra rotina também se encontra implementada e pode ser vista logo abaixo:

inline void GetPositionXY(int &X, int &Y)
{
        X = Position.X;
        Y = Position.Y;
}

Mas voltando a classe C_OrderView temos um ponto interessante que também merece atenção:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong           ticket;
        double          price, pp, pt, ps;
        eHLineTrade     hl;
                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        MoveTo(Mouse.GetButtonStatus());
                        break;

// ... Restante do código ....

}

A rotina MoveTo pode ser vista pouco acima neste mesmo artigo, ela também faz parte da classe C_OrderView, mas a grande questão é a rotina Mouse.GetButtonsStatus, esta rotina irá retornar o status dos botões e teclas ligadas a eventos do mouse.

Esta rotina Mouse.GetButtonStatus pode ser vista logo abaixo:

inline uint GetButtonStatus(void) const
{
        return Position.ButtonsStatus;
}

Trata-se de apenas uma única linha que retorna uma variável que contém os valores gravados do último evento do mouse, mas nos vamos chegar neste código que irá gravar este valor, mas primeiro, vamos ver o código de inicialização do mouse, pois é isto mesmo, temos que dizer ao EA que queremos inicializar o mouse e que o EA será o responsável por tratar de varias coisas envolvidas com o mouse a partir deste momento. O código responsável por isto é visto no fragmento abaixo:

// ... Outras coisas ....

input group "Mouse"
input color     user50 = clrBlack;      //Linha de Preço
input color     user51 = clrDarkGreen;  //Estudo Positivo
input color     user52 = clrMaroon;     //Estudo Negativo
//+------------------------------------------------------------------+

// ... Informações gerais ....

//+------------------------------------------------------------------+
int OnInit()
{
        static string   memSzUser01 = "";
        
        Terminal.Init();
        WallPaper.Init(user10, user12, user11);
        Mouse.Init(user50, user51, user52);

// ... Restante do código ....

Vejam que definimos 3 cores a serem usadas pelo sistema, estas cores devem ser escolhidas de forma a deixar os dados bem claros e visíveis no gráfico. Mas vamos dar uma olhada no código Mouse.Init para entender um pouco mais, este é visto a seguir.

void Init(color c1, color c2, color c3)
{
        m_Infos.cor01 = c1;
        m_Infos.cor02 = c2;
        m_Infos.cor03 = c3;
        if (m_Infos.szNameObjH != NULL) return;
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_MOUSE_MOVE, true);
        ChartSetInteger(Terminal.Get_ID(), CHART_CROSSHAIR_TOOL, false);
        m_Infos.szNameObjH = "H" + (string)MathRand();
        m_Infos.szNameObjV = "V" + (string)MathRand();
        m_Infos.szNameObjT = "T" + (string)MathRand();
        m_Infos.szNameObjB = "B" + (string)MathRand();
        m_Infos.szNameObjI = "I" + (string)MathRand();
//---
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjH, OBJ_HLINE, 0, 0, 0);
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjV, OBJ_VLINE, 0, 0, 0);
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjT, OBJ_TREND, 0, 0, 0);
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjB, OBJ_BITMAP, 0, 0, 0);
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjI, OBJ_TEXT, 0, 0, 0);
//---
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjH, OBJPROP_TOOLTIP, "\n");
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_TOOLTIP, "\n");
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_TOOLTIP, "\n");
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_TOOLTIP, "\n");
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_TOOLTIP, "\n");
//---
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_WIDTH, 2);
//---
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_BMPFILE, "::" + def_Fillet);
//---
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_FONT, "Lucida Console");
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_FONTSIZE, 10);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_BACK, false);
        Hide();
        Show();
}

Este código não tem nada de muito anormal, estamos apenas criando alguns objetos para serem utilizados pela classe, mas o fragmento destacado pode gerar duvidas, já que se você procurar ele na classe não irá encontrar nenhum ponto onde ele está declarando, isto por que ele de fato esta declarado no código do arquivo do EA junto com as declarações de outro recurso, futuramente irie agrupar tudo isto em um arquivo, mas por enquanto vai ficando desta forma. Então se você olhar o código do EA irá encontrar as seguintes linhas:

#define def_Resource    "Resources\\SubSupport.ex5"
#define def_Fillet      "Resources\\Fillet.bmp"
//+------------------------------------------------------------------+
#resource def_Resource
#resource def_Fillet

Ou seja, ai está o tal recurso usado lá na linha que foi destacada no código de inicialização do mouse.

Bem chegamos ao nosso auge dentro desta classe é este é chamado pelo fragmento a seguir:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Mouse.DispatchMessage(id, lparam, dparam, sparam);
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        ChartRedraw();
}

Então a linha em destaque no código do EA irá chamar o seguinte código:

void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        int     w = 0;
        uint    key;
        static int b1 = 0;
        static double memPrice = 0;
                                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Position.X = (int)lparam;
                        Position.Y = (int)dparam;
                        ChartXYToTimePrice(Terminal.Get_ID(), Position.X, Position.Y, w, Position.dt, Position.price);
                        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjH, 0, 0, Position.price = Terminal.AdjustPrice(Position.price));
                        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjV, 0, Position.dt, 0);
                        key = (uint) sparam;
                        if ((key & 0x10) == 0x10)
                        {
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_COLOR, m_Infos.cor01);
                                b1 = 1;
                        }
                        if (((key & 0x01) == 0x01) && (b1 == 1))
                        {
                                ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, false);
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, m_Infos.cor01);
                                ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjT, 0, Position.dt, memPrice = Position.price);
                                b1 = 2;
                        }
                        if (((key & 0x01) == 0x01) && (b1 == 2))
                        {
                                ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjT, 1, Position.dt, Position.price);
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, (memPrice > Position.price ? m_Infos.cor03 : m_Infos.cor02));
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_COLOR, (memPrice > Position.price ? m_Infos.cor03 : m_Infos.cor02));
                                ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjB, 0, Position.dt, Position.price);
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_ANCHOR, (memPrice > Position.price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
                                ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_TEXT, StringFormat("%.2f ", Position.price - memPrice));
                                ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjI, 0, Position.dt, Position.price);
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_ANCHOR, (memPrice > Position.price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
                        }
                        if (((key & 0x01) != 0x01) && (b1 == 2))
                        {
                                b1 = 0;
                                ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, true);
                                Hide();
                                Show();
                        }
                        Position.ButtonsStatus = (b1 == 0 ? key : 0);
                        break;
        }
}

Este código acima, não e repito não é um código totalmente implementando, ele irá dar suporte e resolver apenas as questões básicas para o EA até este estagio de desenvolvimento. Para entender reparem uma coisa no código de inicialização do mouse, lá temos a seguinte linha:

ChartSetInteger(Terminal.Get_ID(), CHART_CROSSHAIR_TOOL, false);

o que esta linha faz é evitar que a CRUZ de analise seja mostrada quando clicamos com o botão do meio do mouse. Mas por que evitar que a cruz seja criada ?!?! Para entender isto veja a animação baixo:

Reparem que estamos no WDO, ele se move de 0.5 em 0.5, mas ao tentar fazer uma analise, vemos que não temos muita precisão, e em alguns casos temos que ter uma certa precisão para executar uma analise, e esta cruz que o MT5 nos fornece não é adequada o suficiente para casos específicos, precisamos recorrer a um novo sistema, por isto impedimos o MT5 de criar a cruz para nos quando o EA está em funcionamento, o que fazemos é criar a nossa própria cruz para fazer a analise, com isto podemos adicionar dados e valores que nos são relevantes, e apresentar eles de forma que achemos mais adequado. Isto pode ser visto abaixo, onde é demonstrado o resultado do uso do sistema de modelagem dos dados quando o EA esta em funcionamento.


Vejam que os valores indicados correspondem aos valores exatos de movimento, e temos uma indicação visual se o valor foi positivo, que no caso a indicação fica verde, ou negativo que faz a indicação ficar vermelha, o arrasto é criado e os pontos tanto inicial, quanto final são facilmente visíveis, mas como eu disse, o sistema não é um sistema completo, ainda, mas você pode implementar melhorias nele se assim desejar e precisar, até uma nova versão da classe C_Mouse sair você pode melhorar esta versão e ter mais dados que você acha necessário ter, mas para fazer isto, é preciso entender como as coisas acontecem, então vamos olhar com mais calma o código de mensagem da classe C_Mouse. 


4.0.1 Entendendo o código DispathMessage da classe C_Mouse

Bem o código começa capturando e ajustando os valores das variáveis de posição do mouse, isto é feito pelo fragmento abaixo:

Position.X = (int)lparam;
Position.Y = (int)dparam;
ChartXYToTimePrice(Terminal.Get_ID(), Position.X, Position.Y, w, Position.dt, Position.price);

Os valores de posição são informados pela própria plataforma MT5, mas estes valores vem do sistema operacional, então eles estão em coordenadas de tela, ou seja X e Y, mas precisamos converter eles para coordenadas de gráfico e para isto utilizamos a rotina ChartXYToTimePrice que o MQL5 nos fornece, facilitando em muito a nossa vida.

Feito isto fazemos a movimentação das linhas de preço e tempo

ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjH, 0, 0, Position.price = Terminal.AdjustPrice(Position.price));
ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjV, 0, Position.dt, 0);

mas a linha de tempo é inicialmente invisível para nos, então não podemos de fato vê-la no gráfico inicialmente. Depois disto capturamos o status do mouse, neste ponto

key = (uint) sparam;

Até aqui tudo bem, agora vamos fazer o seguinte, vamos testar se o botão do meio foi pressionado, e se este for o caso, iremos a linha de tempo passará a ser visível no gráfico, e isto é conseguido pelo código abaixo:

if ((key & 0x10) == 0x10)
{
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_COLOR, m_Infos.cor01);
        b1 = 1;
}

Ao fazermos isto, usamos uma variável static para armazenar este evento, então a partir de agora nenhum outro evento será mais aceito e tratado pelo EA, ele irá tratar do estudo que queremos fazer no gráfico. Mas o estudo de fato somente se inicia quando pressionarmos o botão esquerdo do mouse, ou seja, estou usando o mesmo modo de funcionamento já conhecido por todos usuários da plataforma MT5 para fazer estudos, e isto é o mais adequado, já que ter que aprender um novo modo de executar os estudos pode levar você a desistir do sistema. Então o EA fica esperando este clique no botão esquerdo e isto é feito pelo seguinte fragmento

if (((key & 0x01) == 0x01) && (b1 == 1))
{
        ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, false);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, m_Infos.cor01);
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjT, 0, Position.dt, memPrice = Position.price);
        b1 = 2;
}

quando o clique acontece, o sistema de movimentação do gráfico é travado, depois apresentamos uma linha de tendência para indicar os pontos do estudo, e o sistema passa para a próxima etapa e isto é informado com um novo valor em b1. Agora é de fato a parte que você pode adicionar mais informações ou colocar as que você achar mais relevantes, aqui estou apenas demonstrando o sistema, mas sinta-se livre para colocar o que você quiser e sentir necessidade, e isto deve ser feito no seguinte ponto:

if (((key & 0x01) == 0x01) && (b1 == 2))
{
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjT, 1, Position.dt, Position.price);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, (memPrice > Position.price ? m_Infos.cor03 : m_Infos.cor02));
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_COLOR, (memPrice > Position.price ? m_Infos.cor03 : m_Infos.cor02));
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjB, 0, Position.dt, Position.price);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_ANCHOR, (memPrice > Position.price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_TEXT, StringFormat("%.2f ", Position.price - memPrice));
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjI, 0, Position.dt, Position.price);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_ANCHOR, (memPrice > Position.price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
}

Observem a linha em destaque, pois é justamente ali é que é apresentado e calculado o valor que você vê na tela do gráfico, você pode adicionar várias coisas ai, sinta-se livre para fazer isto. O funcionamento desta parte faz com que os dados sejam calculados e apresentados enquanto o botão esquerdo estiver pressionado, então você terá o mesmo comportamento que é o padrão da plataforma MT5, só que com os valores ajustados e modelados conforme o seu desejo e necessidade.

A próxima coisa a ser feita é mais um teste, que é visto abaixo

if (((key & 0x01) != 0x01) && (b1 == 2))
{
        b1 = 0;
        ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, true);
        Hide();
        Show();
}

Depois de liberar o botão esquerdo do mouse, o gráfico é liberado e pode ser arrastado, assim como todos os elementos usados para criar o estudo são ocultados e voltamos a ter apenas a linha de preço visível novamente. E para encerrar temos um último fragmento que pode ser visto na linha abaixo:

Position.ButtonsStatus = (b1 == 0 ? key : 0);

Se nenhum estudo esta sendo chamado, então o status das teclas do mouse são armazenados e podem ser usados em outros pontos do EA, mas caso um estudo tenha sido chamado, um valor NULO é usado como dado de status, desta forma não será possível criar ordens, ou mexer na posição das mesmas.

No video abaixo, você poderá notar o como este rastro realmente funciona e como o volume vai sendo montado e ajustado na tela, este indicador ajuda muito, é bom que você aprenda a utilizá-lo da forma correta, pois junto com o Times & Trade ele forma a dupla do barulho no tape reading que é uma das modalidades mais avançadas para se operar o mercado.




Arquivos anexados |
EA_-_Mouse.zip (5986.31 KB)
Desenvolvendo um EA de negociação do zero( Parte 15): Acessando dados na WEB (I) Desenvolvendo um EA de negociação do zero( Parte 15): Acessando dados na WEB (I)
Como ter acesso a dados na WEB dentro do MetaTrader 5. Na WEB temos diversos sites e locais onde uma grande e vasta quantidade de informações estão disponíveis e ficam acessíveis a aqueles que sabem onde procurar e como melhor utilizar estas informações.
Desenvolvendo um EA de negociação do zero (Parte 13): Times And Trade (II) Desenvolvendo um EA de negociação do zero (Parte 13): Times And Trade (II)
Construa um sistema de Times & Trade para analisar o mercado. Esta é a segunda parte. No artigo anterior Times & Trade ( I ) apresentei um sistema alternativo para montar um gráfico de forma a você ter um indicador que lhe permitisse interpretar os negócios que foram executados no mercado de forma o mais rápido possível.
Desenvolvendo um EA de negociação do zero (Parte 16): Acessando dados na WEB (II) Desenvolvendo um EA de negociação do zero (Parte 16): Acessando dados na WEB (II)
Como levar os dados da WEB para dentro de um EA . O caminho para fazer isto não é tão obvio, ou melhor dizendo, tão simples a ponto de você conseguir fazer, sem de fato conhecer e entender todos os recursos que estão presentes no MetaTrader 5.
Como e por que desenvolver seu próprio sistema de negociação algorítmica Como e por que desenvolver seu próprio sistema de negociação algorítmica
Neste artigo, abordaremos os conceitos básicos da linguagem de programação MQL. O objetivo do artigo é ajudar os programadores iniciantes a desenvolver seu próprio sistema de negociação algorítmico (Expert Advisor).