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

Desenvolvendo um EA de negociação do zero (Parte 10): Acessando indicadores personalizados

MetaTrader 5Sistemas de negociação | 13 abril 2022, 09:36
1 171 0
Daniel Jose
Daniel Jose

Introdução

Um EA de negociação, só será realmente bem explorado se for possível você usar indicadores personalizados nele, caso contrário ele será apenas um conjunto de códigos e instruções, mais ou menos bem elaborados, que nos ajuda de alguma forma a montar nossas posições e executar operações de mercado, não indo além disto.

Mas adicionar indicadores ao gráfico do MetaTrader 5 não é a parte mais complexa, mas conseguir acessar os dados calculados por estes indicadores diretamente em um EA, sem o devido planejamento se torna uma tarefa praticamente impossível, e se você não souber como fazer isto, estará limitado aos indicadores que estão disponíveis na linguagem MQL5, e aqueles podem não ser exatamente os que você precisa, exemplo disto é a VWAP que é uma média muito importante para quem opera futuros na Bolsa de valores no BRASIL e apesar desta media não estar disponível por padrão entre os indicadores do MetaTrader  , nada impede de você criar um indicador personalizado que execute o calculo e apresente a VWAP na tela, mas a coisa se torna muito complicada se você deseja usar este mesmo indicador em um sistema que estará sendo analisado em um EA, sem o devido conhecimento você não conseguirá de fato usar um indicador personalizado dentro de um EA, mas aqui irei explicar como contornar e resolver esta tarefa.


Planejamento

A primeira coisa a ser feita é procurar criar o calculo que usaremos no indicador personalizado, felizmente o calculo da VWAP que irei usar como exemplo é algo bem simples


Traduzindo isto em linguagem de programação, no caso MQL5 temos o seguinte:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
        double          Price = 0;
        ulong           Volume = 0;
        static int      siPos = 0;

        if (macroGetDate(time[rates_total - 1]) != macroGetDate(time[siPos]))
        {
                for (int c0 = rates_total - 1; macroGetDate(time[siPos]) != macroGetDate(time[c0]); siPos++);
                ArrayInitialize(VWAP_Buff, EMPTY_VALUE);
        }
        for (int c0 = siPos; c0 < rates_total; c0++)
        {
                Price += ((high[c0] + low[c0] + close[c0]) / 3) * volume[c0];
                Volume += volume[c0];
                VWAP_Buff[c0] = Price / Volume;
        }

    return rates_total;
}

O calculo em si esta em destaque, já o restante da função é usada para inicializar de forma adequada a VWAP DIÁRIA. bem mas apesar disto nosso indicador ainda não pode ser colocado no gráfico, temos que adicionar mais algumas coisinhas ao código, e o restante do código pode ser visto abaixo:

#property copyright "Daniel Jose - Indicador VWAP ( IntraDay )"
#property version "1.01"
#property indicator_chart_window
#property indicator_buffers     1
#property indicator_plots       1
#property indicator_width1      2
#property indicator_type1 	DRAW_LINE
#property indicator_color1 	clrBlack
//+------------------------------------------------------------------+
#define macroGetDate(A) (A - (A % 86400))
//+------------------------------------------------------------------+
double VWAP_Buff[];
//+------------------------------------------------------------------+
int OnInit()
{
        SetIndexBuffer(0, VWAP_Buff, INDICATOR_DATA);   
        
        return INIT_SUCCEEDED;
}

Com isto temos a possibilidade da VWAP no gráfico conforme pode ser visto abaixo:


Bom esta foi a parte fácil, mas agora temos que conseguir uma forma de fazer o EA enxergar a VWAP para assim a conseguir analisar ela de alguma forma especifica para tirar proveito e nos auxiliar durante as operações.

Para facilitar nossa vida, vamos deixar a VWAP em um local de fácil acesso, então decidi deixar ela no local indicado.


Feito isto vamos nos aventurar em uma nova forma de projetar as coisas. Apesar do nosso indicador VWAP esta correto em essência ele não esta corretamente programado para ser usado em um EA. Você pode estar pensando: Mas como assim ?!?! O problema é que o EA não irá conseguir saber se o indicador esta ou não no gráfico e sem saber disto ele não irá conseguir ler o indicador.

O problema é que o nome do arquivo pouco importa para o sistema, você pode colocar qualquer nome no arquivo, mas o nome do indicador deve refletir o que ele esta calculando, e nosso indicador não tem ainda um nome que reflete isto, apesar de ele ser chamado VWAP, para o sistema isto é irrelevante, por este motivo o EA não irá conseguir saber se ele esta ou não presente no gráfico.

Para que o indicador possa refletir o que ele esta calculando, temos que indicar isto no código do mesmo, desta forma iremos criar um nome único e que não necessariamente estará ligado ao nome do arquivo. No nosso caso, o código de inicialização do indicador deveria ficar assim:

int OnInit()
{
        SetIndexBuffer(0, VWAP_Buff, INDICATOR_DATA);   
        IndicatorSetString(INDICATOR_SHORTNAME, "VWAP");
        
        return INIT_SUCCEEDED;
}

Adicionando apenas a linha em destaque, já resolve o nosso problema, existem casos em que a coisa pode ser um pouco mais complicada, veremos isto mais a frente neste artigo, mas veja por exemplo o código do indicador CUSTOM MOVING AVERAGE presente na biblioteca do MetaTrader  , que pode ser visto abaixo seu fragmento de inicialização: 

void OnInit()
{
	SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
	IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);
	PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,InpMAPeriod);
	PlotIndexSetInteger(0,PLOT_SHIFT,InpMAShift);

	string short_name;
	switch(InpMAMethod)
	{
      		case MODE_EMA :
			short_name="EMA";
         		break;
      		case MODE_LWMA :
	         	short_name="LWMA";
	         	break;
      		case MODE_SMA :
         		short_name="SMA";
         		break;
      		case MODE_SMMA :
         		short_name="SMMA";
         		break;
      		default :
         		short_name="unknown ma";
     	}
   	IndicatorSetString(INDICATOR_SHORTNAME, short_name + "(" + string(InpMAPeriod) + ")");
   	PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
}

A parte que indica este nome é marcado em destaque, notem que é algo que nada tem haver com o nome do arquivo, mas é muito importante ser feito dentro de um indicador personalizado.

Agora que fizermos, e garantimos que o EA de fato irá conseguir verificar se o indicador personalizado esta ou não no gráfico, podemos passar para o próximo passo.


Acessando o Indicador via EA

Apesar de poder fazer as coisas como venho fazendo, o ideal para se entender de fato o que esta acontecendo, é criar um código completamente novo, mas já que a ideia é ir desenvolvendo um EA de negociação do zero, e teremos que passar por este ponto para continuar a caminhada, vou criar um EA isolado, você poderá adicionar ou não isto futuramente no seu código final. Bem feito esta ressalva vamos a codificação. Nosso EA começa com um código LIMPO, visto abaixo:

//+------------------------------------------------------------------+
int OnInit()
{
        EventSetTimer(1);
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        EventKillTimer();
}
//+------------------------------------------------------------------+
void OnTick(){}
//+------------------------------------------------------------------+
void OnTimer(){}
//+------------------------------------------------------------------+

Vamos fazer o seguinte: No primeiro momento vamos supor que o Indicador VWAP esta no gráfico, e vamos carregar o último valor calculado pelo indicador para dentro do EA, isto será feito a cada um segundo. Mas como vamos fazer isto ?!?! É bem simples, veja como fica o código do EA depois de ser modificado para refletir isto:

#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
int     handle;
double  Buff[];
//+------------------------------------------------------------------+
int OnInit()
{
        handle = ChartIndicatorGet(ChartID(), 0, "VWAP");
        SetIndexBuffer(0, Buff, INDICATOR_DATA);
        
        EventSetTimer(1);
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        EventKillTimer();
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+
void OnTimer()
{
        int i;
        
        if (handle != INVALID_HANDLE)
        {
                i = CopyBuffer(handle, 0, 0, 1, Buff);
                Print(Buff[0]);
        }
}
//+------------------------------------------------------------------+

As partes em destaque é que foram adicionadas ao código limpo, e o resultado é visto abaixo:


Ai você pensa: O que aconteceu, por que isto funcionou ?!?! O motivo é que o MQL5 nos fornece meios para ler e escrever dados entre sistemas, uma das formas de ler é usando a função CopyBuffer e funciona segundo o esquema baixo:


Isto nos permite ler qualquer informação de qualquer indicador personalizado, ou seja não se limita aos indicadores na biblioteca padrão o MetaTrader 5, você pode criar qualquer indicador e a coisa irá funcionar desde que você siga os passos que mostrei até o momento.

Agora vamos analisar um outro cenário, desta vez o indicador VWAP não esta no gráfico, mas o EA precisa dele então teremos que carregar ele para o gráfico, mas como fazer isto ?!?! Isto também, é igualmente bem simples, e até já usamos isto em outro momento anterior, mas para outro proposito, no caso era criar uma sub janela pelo EA, o que vamos fazer é usar a função iCustom, mas desta vez a intenção é carregar um indicador personalizado, então o código do EA fica conforme mostrado abaixo:

#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
int     handle;
double  Buff[];
//+------------------------------------------------------------------+
int OnInit()
{
        handle = ChartIndicatorGet(ChartID(), 0, "VWAP");
        SetIndexBuffer(0, Buff, INDICATOR_DATA);
        
        EventSetTimer(1);
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        EventKillTimer();
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+
void OnTimer()
{
        int i;
        
        if (handle == INVALID_HANDLE) handle = iCustom(NULL, PERIOD_CURRENT, "VWAP.EX5");else
        {
                i = CopyBuffer(handle, 0, 0, 1, Buff);
                Print(Buff[0]);
        }
}
//+------------------------------------------------------------------+

O código em destaque é a única adição que fizermos no sistema original, e ao executarmos o EA acima obtermos o seguinte resultado:


o que fizemos pode ser visto na imagem abaixo:

Isto é tudo que você precisa no nível mais básico da coisa, mas se você reparar não é possível ver a VWAP no gráfico, mesmo que o EA esteja de fato usando ela, o usuário não sabe o que esta acontecendo, mas podemos corrigir isto facilmente, e o código final fica como mostrado abaixo, lembre-se de uma coisa, é sempre bom podermos analisar e observar o que o EA esta fazendo, deixar ele solto não é algo muito seguro ou aconselhável:

#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
int     handle;
long    id;
double  Buff[];
string  szCmd;
//+------------------------------------------------------------------+
int OnInit()
{
        szCmd = "VWAP";
        handle = ChartIndicatorGet(id = ChartID(), 0, szCmd);
        SetIndexBuffer(0, Buff, INDICATOR_DATA);
        
        EventSetTimer(1);
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        ChartIndicatorDelete(id, 0, szCmd);
        IndicatorRelease(handle);
        EventKillTimer();
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+
void OnTimer()
{
        int i;
        
        if (handle == INVALID_HANDLE)
        {
                if ((handle = iCustom(NULL, PERIOD_CURRENT, "VWAP.EX5")) != INVALID_HANDLE)
                        ChartIndicatorAdd(id, 0, handle);
        }else
        {
                i = CopyBuffer(handle, 0, 0, 1, Buff);
                Print(Buff[0]);
        }
}
//+------------------------------------------------------------------+

Este código EA acima irá ler o último valor calculado pelo indicador VWAP e irá mostrar ele na tela, caso ele não esteja no gráfico ele irá carregar o indicador para o EA, e irá apresentar ele na tela. Quando o EA for retirado, ele irá retirar a VWAP da tela, ou seja iremos manter o EA sempre com as coisas que ele precisa para executar seus cálculos e assim nos auxiliar da melhor forma possível. O resultado do que expliquei pode ser visto abaixo:


Apesar de tudo, você pode achar que isto não é muito viável, já que aparentemente não fizemos nenhum ajuste no indicador, mas usando apenas o que eu mostrei é possível fazer qualquer coisa relacionada a indicadores personalizados, para exemplificar isto e por um ponto final de esclarecimento vou mostrar uma coisa diferente, vamos aplicar uma média móvel e usar o EA da mesma forma que fizemos com a VWAP, só que iremos indicar parâmetros para a média.


Um Segundo caso: Usando Médias Móveis

O calculo da média móvel não vem ao caso, quero que foquem no que realmente importa, como passar parâmetros para dentro de um indicador personalizado, então nosso novo indicador personalizado ficaria assim:

#property copyright "Daniel Jose 16.05.2021"
#property description "Medias Moveis Basicas ( Calculo otimizado )"
#property indicator_chart_window
//+------------------------------------------------------------------+
enum eTypeMedia
{
        MME,    //Media Movel Exponencial
        MMA     //Media Movel Aritimetica
};
//+------------------------------------------------------------------+
#property indicator_buffers             1
#property indicator_plots               1
#property indicator_type1               DRAW_LINE
#property indicator_width1              2
#property indicator_applied_price       PRICE_CLOSE
//+------------------------------------------------------------------+
input color      user00 = clrRoyalBlue; //Cor
input int        user01 = 9;            //Periodos
input eTypeMedia user02 = MME;          //Tipo de Media
input int        user03 = 0;            //Deslocamento
//+------------------------------------------------------------------+
double Buff[], f_Expo;
//+------------------------------------------------------------------+
int OnInit()
{
        string sz0 = "MM" + (user02 == MME ? "E" : (user02 == MMA ? "A" : "_")) + (string)user01;
        
        f_Expo = (double) (2.0 / (1.0 + user01));
        ArrayInitialize(Buff, EMPTY_VALUE);
        SetIndexBuffer(0, Buff, INDICATOR_DATA);
        PlotIndexSetInteger(0, PLOT_LINE_COLOR, user00);
        PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, user01);
        PlotIndexSetInteger(0, PLOT_SHIFT, user03);
        IndicatorSetString(INDICATOR_SHORTNAME, sz0);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        double Value;
        int c0;

        switch (user02)
        {
                case MME:
                        if (user01 < rates_total)
                        {       
                                for (c0 = (prev_calculated > 0 ? prev_calculated - 1 : 0); c0 < rates_total - user03; c0++)
                                        Buff[c0] = (c0 > 0 ? ((price[c0] - Buff[c0 - 1]) * f_Expo) + Buff[c0 - 1] : price[c0] * f_Expo);
                                for (; c0 < rates_total; c0++)
                                        Buff[c0] = EMPTY_VALUE;
                        }
                        break;
                case MMA:
                        if (user01 < rates_total)
                        {
                                if (prev_calculated == 0) 
                                {       
                                        Value = 0;
                                        for (int c1 = 0; c1 < user01; c1++) Value += price[user01 - c1];
                                        Buff[user01] = Value / user01;
                                }
                                for (c0 = (prev_calculated > 0 ? prev_calculated - 1 : user01 + 1); c0 < rates_total - user03; c0++)
                                        Buff[c0] = ((Buff[c0 - 1] * user01) - price[c0 - user01] + price[c0]) / user01;
                                for (; c0 < rates_total; c0++)
                                        Buff[c0] = EMPTY_VALUE;
                        }
                        break;
        }
        
        return rates_total;
}
//+------------------------------------------------------------------+

Bem, agora o nome do indicador irá depender de algumas coisas, então podemos fazer com que o EA verifique e se ajuste a cada uma das situações. Para exemplificar vamos fazer com que nosso EA use 2 médias móveis e elas deverão ser apresentadas no gráfico, o código do indicador é mostrado acima, mas observe as partes em destaque, é justamente elas que permitem ao EA, e no caso a função iCustom, mudar e ajustar os parâmetros do indicador, é muito importante entender isto, para que você consiga fazer isto quando for necessário. Então uma das médias será uma média exponencial de 17 períodos e a outra de 52 períodos e aritmética, a média de 17 queremos que seja verde e a de 52 Vermelha. então o EA irá ver o indicador como sendo uma função da seguinte forma:

MEDIA ( COR, PERIODO, TIPO, DESLOCAMENTO ) ou seja, você não deve mais pensar no indicador como sendo um arquivo distinto, mas como sendo uma função do EA, isto é muito comum em programação, onde chamamos um programa com parâmetros adequados de forma a que ele faça uma tarefa especifica, e no final conseguimos os resultados mais facilmente. Mas a questão é: Como faremos para que nosso EA crie e gerencie este cenário da mesma forma que fizemos com a VWAP ?!?!

Para fazer isto nosso EA terá que mudar, o código completo do novo EA pode ser visto abaixo, lembrando que você deve enxergar o indicador como uma função:

#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
long    id;
int     handle1, handle2;
double  Buff1[], Buff2[];
string  szCmd1, szCmd2;
//+------------------------------------------------------------------+
int OnInit()
{
        szCmd1 = "MME17";
        szCmd2 = "MMA52";
        id = ChartID();
        handle1 = ChartIndicatorGet(id, 0, szCmd1);
        handle2 = ChartIndicatorGet(id, 0, szCmd2);
        SetIndexBuffer(0, Buff1, INDICATOR_DATA);
        SetIndexBuffer(0, Buff2, INDICATOR_DATA);
        
        EventSetTimer(1);
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        ChartIndicatorDelete(id, 0, szCmd1);
        ChartIndicatorDelete(id, 0, szCmd2);
        IndicatorRelease(handle1);
        IndicatorRelease(handle2);
        EventKillTimer();
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+
void OnTimer()
{
        int i1, i2;
        
        if (handle1 == INVALID_HANDLE)
        {
                if ((handle1 = iCustom(NULL, PERIOD_CURRENT, "Media Movel.EX5", clrGreen, 17, 0)) != INVALID_HANDLE)
                        ChartIndicatorAdd(id, 0, handle1);
        };
        if (handle2 == INVALID_HANDLE)
        {
                if ((handle2 = iCustom(NULL, PERIOD_CURRENT, "Media Movel.EX5", clrRed, 52, 1)) != INVALID_HANDLE)
                        ChartIndicatorAdd(id, 0, handle2);
        };
        if ((handle1 != INVALID_HANDLE) && (handle2 != INVALID_HANDLE))
        {
                i1 = CopyBuffer(handle1, 0, 0, 1, Buff1);
                i2 = CopyBuffer(handle2, 0, 0, 1, Buff2);
                Print(Buff1[0], "<< --- >>", Buff2[0]);
        }
}
//+------------------------------------------------------------------+

e o resultado visto na animação a seguir:


Mas reparem nas partes em destaque no código do EA. Isto é exatamente o que queríamos fazer, ou seja, passamos os parâmetros para o indicador usando o mesmo mecanismo que usamos na VWAP, mas no caso da VWAP não foi preciso informar nenhum parâmetro para ela, mas no caso das médias é preciso informar o que desejamos, e o fato de fazer isto nos permite ter um grau muito grande de liberdade, por conta disto não tem como fazer algo focado ou especifico, já que irá depender do que você estará programando e procurando fazer com que o EA analise.


Conclusão

Este artigo é um dos pouco que não tem como eu deixar um código generico disponível para ser usado, mas mesmo assim detalhei 2 EA e 2 indicadores personalizados distintos de forma a demonstrar como usar este tipo de sistema em um EA mais complexo e elaborado. Acredito que o fato de ter feito isto, irá ser o suficiente para que você consiga usar indicadores personalizados desenvolvidos por você, em analises feitas pelo EA, a coisa é bem interessante, e mesmo que alguns continuem a afirmar que o MetaTrader 5 é um sistema básico com poucos recursos, mostrem a estas pessoas que eles estão errados, o MetaTrader 5 é a plataforma mais versátil e completa que um operador pode desejar, mas a falta de conhecimento estas pessoas que insistem em falar bobagens faz com que as coisas não sejam completamente exploradas.

Aproveitem o conhecimento que mostrei neste artigo, pois o MetaTrader 5 permite ir muito além do que muitos de vocês foram até o momento.

Então até um próximo artigo.


Referencias externas

Como Chamar indicadores no MQL5

Indicadores personalizados no MQL5 para novatos

MQL5 para Novatos: Guia para o uso de indicadores técnicos em expert Advisor



Arquivos anexados |
Desenvolvendo um EA de negociação do zero (Parte 11): Sistema CROSS ORDER Desenvolvendo um EA de negociação do zero (Parte 11): Sistema CROSS ORDER
Criando um sistema cross order. Existem uma classe de ativos que dificulta muito a vida dos operadores, estes são os ativos de contrato futuro, e por que eles dificultam a vida do operador ?
Desenvolvendo um EA de negociação do zero (Parte 09): Um salto conceitual (II) Desenvolvendo um EA de negociação do zero (Parte 09): Um salto conceitual (II)
Colocando o Chart Trade em uma janela flutuante. No artigo anterior criamos o sistema base para utilizar templates dentro de uma janela flutuante.
WebSocket para o MetaTrader 5: Usando a API do Windows WebSocket para o MetaTrader 5: Usando a API do Windows
Neste artigo vamos usar WinHttp.dll com o intuito de criar um cliente WebSocket para os programas MetaTrader 5. Em última instância, o cliente deve ser implementado como uma classe e testado em interação com o WebSocket API da Binary.com.
Desenvolvendo um EA de negociação do zero (Parte 08): Um salto conceitual (I) Desenvolvendo um EA de negociação do zero (Parte 08): Um salto conceitual (I)
Como implementar de forma o mais simples possível uma nova funcionalidade ? Aqui iremos dar um passo para trás para logo em seguida dar dois para frente.