English Русский 中文 Español Deutsch 日本語
preview
Desenvolvendo um EA de negociação do zero (Parte 30): CHART TRADE agora como indicador ?!

Desenvolvendo um EA de negociação do zero (Parte 30): CHART TRADE agora como indicador ?!

MetaTrader 5Exemplos | 7 setembro 2022, 15:47
778 0
Daniel Jose
Daniel Jose

1.0 - Introdução

No artigo anterior Desenvolvendo um EA de negociação do zero ( Parte 29 ) retiramos o Chart Trade de dentro do EA, já havíamos feito isto com outras coisas como o Volume At Price e o Times & Trade, isto para melhorar a performance e robustez do nosso EA. Pois bem, ao retirarmos o Chart Trade de dentro do EA, ficamos apenas com o sistema básico de ordens que apesar de parecer insuficiente para alguns, consegue de fato dar conta de todo o trabalho, mas existem pessoas que gostam de entrar e sair das operações a mercado, não gostam de ficar posicionando ordens de maneira que elas fiquem pendentes, aguardando o preço atingir um determinado patamar para assim entra ou sair da operação.

Quando estamos usando a plataforma MetaTrader 5 no ativo que de fato estamos operamos, digo isto pois você pode estar usando um sistema de ordens cruzadas, e vimos como fazer isto na ( Parte 11 ) desta sequencia, teremos acesso ao QUICK TRADING que são dois botões que nos permite enviar ordens a mercado, você pode observar eles no canto superior esquerdo e eles tem a aparência próximo da imagem abaixo:

Estes funcionam como um Chart Trading básico, mas estes mesmos botões não irão ser adequados ou se quer irão aparecer, caso você esteja usando um sistema de ordens cruzadas, neste caso temos que voltar com o nosso Chart Trade, mas não iremos mais usá-lo dentro do EA, ele não fará mais parte do código do EA, ele a partir deste momento passará a ser um mero indicador.

Mas por que transformar o Chart Trading em um indicador ?!?! O motivo disto é que um EA deve apenas ser responsável pelo sistema de ordens, qualquer coisa que não faça parte deste sistema deve ser retirado de alguma forma do código do EA, talvez isto não faça sentido para você neste momento, mas em breve você entenderá isto em mais detalhes, já que estou preparando um sequencia para explicar justamente o motivo de fazer isto.

Você poderia até mesmo fazer com que o Chart Trading fosse usado como um script, mas isto nos traria um inconveniente: O de que cada vez que o tempo gráfico fosse modificado, o script seria encerrado nos forçando a recoloca-lo novamente no gráfico de maneira manual,

No entanto usando um indicador, não teremos mais este inconveniente, mesmo por que o Chart Trade não irá afetar em nada o Thread de execução dos indicadores, e fazendo desta forma, o EA ficará livre podendo ter o seu código sendo focado apenas e somente no que diz respeito a gerenciar as ordens e posições.

É bem verdade que não iremos ter todos aqueles controles e informações da versão original, este Chart Trade na forma de indicador é bem mais simples, mas ainda assim adequado, mesmo por que o sistema do MetaTrader 5 também é bem simples, porém funcional, mas não temos acesso a algumas informações que iremos ter no nosso Chart Trade.

Então mãos a obra, pois a coisa aqui vai ser interessante...


2.0 - Construindo o Indicador Chart Trade

As mudanças necessárias para de fato fazer isto não são poucas, na verdade elas são um numero bastante grande até de fato conseguirmos ter o Chart Trade de volta no gráfico, mas não fazendo parte do código do EA.

A primeira vista você pode pensar que algo como mostrado no fragmento abaixo já seria o suficiente para que o Chart Trade fosse pelo menos compilado, mas não, ele esta intrinsecamente ligado a diversas coisas das quais devemos desligar, mas não cancelar, já que podemos querer que ele volte para dentro do EA.

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\SubWindow\C_TemplateChart.mqh>
//+------------------------------------------------------------------+
C_TemplateChart Chart;
C_Terminal                      Terminal;
//+------------------------------------------------------------------+
int OnInit()
{
        Chart.AddThese("IDE(,,170, 240)");

        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+

Ao tentar compilar este código você irá receber uma serie enorme de erros e falhas, mas vamos corrigir elas uma a uma, de foram a manter a retrocompatibilidade do código com o EA, ao mesmo tempo que ajustamos ele para ser usado como um indicador.

A primeira coisa a fazer é isolar o sistema que criar ou gerencia sub janelas, já que o indicador de Chart Trade não irá usar estas sub janelas, não precisamos deste código embutido no indicador, isto é bastante simples de ser feito. No arquivo C_Chart_IDE.mqh, fazemos a seguinte modificação de forma que agora o código ficará conforme mostrado abaixo:

#ifdef def_INTEGRATION_CHART_TRADER
        #include <NanoEA-SIMD\SubWindow\C_SubWindow.mqh>
        #include <NanoEA-SIMD\Trade\Control\C_IndicatorTradeView.mqh>
#else
        #include <NanoEA-SIMD\SubWindow\C_ChartFloating.mqh>
        #include <NanoEA-SIMD\Auxiliar\C_Terminal.mqh>
#endif  
//+------------------------------------------------------------------+
#ifdef def_INTEGRATION_CHART_TRADER
        class C_Chart_IDE : public C_SubWindow
#else 
        class C_Chart_IDE : public C_ChartFloating
#endif 

Desta forma isolamos completamente o sistema de sub janelas, ao mesmo tempo que impedimos qualquer ligação do Chart Trade com o sistema indicador de ordem do EA. Notem que a definição def_INTEGRATION_CHART_TRADER irá controlar isto, mas já que esta definição é usada apenas no EA, qualquer coisa dentro dela não será compilada no indicador.

Já que o indicador não irá usar sub janelas, temos que contornar algumas coisas e uma delas é vista logo abaixo:

                bool Create(bool bFloat)
                        {
                                m_CountObject = 0;
                                if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false;
                                FileReadInteger(m_fp, SHORT_VALUE);
                                
                                for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
                                m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA());
                                m_szLine = "";
                                while (m_szLine != "</chart>")

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

A função GetIdSubWinEA, irá retornar o numero da janela em que o indicador estará, e esta chamada é executada em muitos pontos distintos. Temos duas soluções para isto: A primeira seria em cada ponto que esta chamada ocorre-se iriamos colocar algo parecido com a modificação vista abaixo, da mesma função vista logo acima.

                bool Create(bool bFloat)
                        {
                                m_CountObject = 0;
                                if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false;
                                FileReadInteger(m_fp, SHORT_VALUE);
                                
                                for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
#ifdef def_INTEGRATION_CHART_TRADER
                                m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA());
#else 
                                m_SubWindow = 0;
#endif 

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

Isto corrigiria o problema, mas teríamos que fazer tantas mudanças que deixaria o código muito complicado de ser entendido depois de um tempo, já que existiria uma quantidade muito grande destas diretivas condicionais de compilação, então tenho uma solução consideravelmente mais simples, porém igualmente eficaz: Fazer uma " emulação " desta e de qualquer outra chamada via definição, então iriamos precisar apenas adicionar o seguinte fragmento ao código do sistema.

#ifndef def_INTEGRATION_CHART_TRADER
        #define GetIdSubWinEA() 0
        #define ExistSubWin() false
#endif 

Este simples código irá resolver o nosso problema com relação as chamadas, com isto bastará mais alguns poucos detalhes para conseguirmos compilar o nosso indicador.

O nosso segundo maior problema é com relação as rotinas envolvidas aos eventos do Mouse, não do mouse em si, mas da classe C_Mouse.

Vamos entender o seguinte: Quando o Chart Trade fazia parte do EA, era possível ter acesso as posições do mouse ( entre outras coisas ), mas se simplesmente adicionarmos a classe C_Mouse no indicador teremos um conflito de interesses entre o indicador e o EA. No momento não irei mostrar como resolver este conflito, mas vamos tomar uma outra direção, apenas de forma temporária, a ponto de resolver as coisas e conseguir ter algumas das funcionalidades que havia no Chart Trade original.

Para isto teremos que trazer algumas coisas da classe C_Mouse para dentro do nosso novo indicador, mas não se preocupe é coisa pouca, a primeira mudança é na classe C_TemplateChart, onde na rotina DispatchMessage iremos fazer a mudança vista logo abaixo:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        datetime dt;
        double p;

        C_Chart_IDE::DispatchMessage(id, lparam, dparam, sparam);
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
#ifdef def_INTEGRATION_CHART_TRADER
                        Mouse.GetPositionDP(dt, p);
#else
                        {
                                int w;
                                ChartXYToTimePrice(Terminal.Get_ID(), (int)lparam, (int)dparam, w, dt, p);
                        }
#endif 
                        for (int c0 = 0; c0 < m_Counter; c0++)  if (m_Info[c0].szVLine != "")

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

Ao adicionar estes trechos demarcados teremos as mesmas funcionalidades como se a classe C_Mouse ainda estivesse presente, e a próxima mudança será na classe C_ChartFloating onde também iremos fazer algo bastante parecido com o que foi visto acima, mas lá temos um código ligeiramente diferente que pode ser apreciado logo abaixo:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        int mx, my;
        datetime dt;
        double p;
        static int six = -1, siy = -1, sic = -1;
                                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
#ifdef def_INTEGRATION_CHART_TRADER
                        Mouse.GetPositionXY(mx, my);
                        if ((Mouse.GetButtonStatus() & 0x01) == 1)
#else 
                        mx = (int)lparam;
                        my = (int)dparam;
                        if (((uint)sparam & 0x01) == 1)
#endif 
                        {
                                if (sic == -1)  for (int c0 = m_MaxCounter - 1; (sic < 0) && (c0 >= 0); c0--)

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

Agora já podemos compilar o indicador e teremos como resultado algo visto no video abaixo:



Mas apesar de tudo ele ainda não esta funcional, temos que tomar uma decisão antes de torna-lo funcional: Vamos deixar o Chart Trade tendo as mesmas capacidades encontradas no EA, ou podemos reduzir estas de forma a ter algo entre o antigo e o encontrado no MetaTrader 5. Como sou radical em relação a isto, irei manter o Chart Trade, com praticamente, as mesmas funcionalidades encontradas no EA, você pode as reduzir se desejar, isto fica ao seu critério.

Tomada esta decisão, podemos partir para o próximo tópico, pois o Chart Trade acaba de se tornar um indicador.


2.1 - Tornando o indicador Chart Trade funcional

Agora o bicho vai pegar, então se acomodem confortavelmente e vamos ver como tornar este indicador funcional, permitindo enviar ordens, fechar posições e até mesmo nos informar o resultado das operações. Pode parecer algo extremamente complicado de ser conseguido, mas não, é algo bastante simples, pois o próprio MetaTrader 5 nos dá o caminho a ser seguido, e isto com o mínimo de esforço.

O que vamos fazer aqui é o mesmo que fizemos no artigo Desenvolvendo um EA do zero ( Parte 16), lá usamos algumas das capacidades do MetaTrader 5, para fazer um sistema Cliente - Servidor interno ( dentro da plataforma ), de forma a transferir dados entre processos diferentes. Aqui iremos fazer algo parecido, só que a modelagem, será ligeiramente diferente, já que teremos que ter uma comunicação bidirecional, e esta deve se manter invisível para o usuário.

O método que usaremos não é o único possível, temos outras formas de fazer isto, uma delas seria usar uma DLL, a fim de fazer esta transferência, mas esta de usar as variáveis do MetaTrader 5, é de longe a mais simples de ser implementada, mantida e modificada conforme se torna necessário.

Uma vez decidido que iremos por este caminho, temos que tomar uma decisão bastante importante: Quem fará o papel de servidor e que será o cliente ?!?! Esta decisão irá implicar em como o sistema de fato deverá se implementado. mas independentemente disto, já teremos o nosso protocolo de mensagens já construído, e ele pode ser visto no fragmento abaixo:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalVariableLeverage      (_Symbol + "_Leverage")
#define def_GlobalVariableTake          (_Symbol + "_Take")
#define def_GlobalVariableStop          (_Symbol + "_Stop")
#define def_GlobalVariableResult        (_Symbol + "_Result")
#define def_GlobalVariableButton        (_Symbol + "_ButtonState")
//+------------------------------------------------------------------+
#define def_ButtonDTSelect              0x01
#define def_ButtonSWSelect              0x02
#define def_ButtonBuyMarket             0x04
#define def_ButtonSellMarket            0x08
#define def_ButtonClosePosition         0x10
//+------------------------------------------------------------------+

E é isto mesmo, nem precisamos definir que fará o papel de cliente ou servidor, para já termos o nosso protocolo de mensagens definido. É importante que este protocolo já esteja definido logo de inicio, pois assim será mais simples desenvolver todo o restante.

Notem que iremos fazer uso de 5 variáveis, para cada ativo que o conjunto Chart Trade - EA estiver presente. E os nomes de tais variáveis, irá ser dependente do ativo a qual o conjunto estiver vinculado, desta forma poderemos usar o conjunto, em mais de um ativo ao mesmo tempo, sem que ninguém interfira com ninguém.

Agora vem o detalhe: Quem fará o trabalho de servidor ?!?! Pois este será responsável por criar tais variáveis. Pessoalmente acho mais prático colocar o EA como servidor, e deixar o Chart Trade como cliente, já que a ideia será sempre ter o EA no gráfico, e o Chart Trade em alguns momentos caso necessário. Desta forma então o EA será o responsável por de fato criar 4 das 5 variáveis, já que uma será responsável por informar qual o botão foi pressionado, sendo assim esta de responsabilidade do Chart Trade criar e manter.

Desta forma, nosso fluxo de dados fica sendo da seguinte maneira:

  1. O EA irá criar as variáveis globais, contendo os valores iniciais que indicam o fator de Alavancagem, o Take financeiro e o Stop Financeiro, eventualmente ele também irá criar uma variável que informa qual o resultado do dia, para que o Chart Trade, possa mostrar isto ao usuário de forma que ele não precisa procurar esta informação em outro local.
  2. O Chart Trade, eventualmente irá criar uma variável, que representa o valor dos botões pressionados, isto irá dizer ao EA o que deverá ser feito: Como fechar uma posição em aberto, ou efetuar uma compra ou venda a mercado. Esta mesma variável, irá existir, apenas e somente, durante este período, deixando de existir assim que a solicitação for concluída pelo EA.

Este fluxo apesar de ser simples, já irá garantir que o Chart Trade, consiga se comunicar com o EA, a ponto de conseguir fazer com que o todo o sistema envie ordens ou encerre posições, da mesma forma como era feito antes.

Para garantir que o Chart Trade, somente irá existir no gráfico caso o EA esteja presente, temos que fazer alguns testes, já que não faz sentido manter o Chart Trade no gráfico, se o EA não estiver disponível para enviar as ordens. Estes testes são executados em 2 momentos:

  • O primeiro é na inicialização do indicador;
  •  O segundo é quando qualquer evento de gráfico acontece.

Mas vamos ver isto no código, desta forma ficará mais simples de entender. Durante a inicialização o seguinte código é executado:

#define def_SHORTNAME "CHART TRADE"
//+------------------------------------------------------------------+
int OnInit()
{
        long lparam = 0;
        double dparam = 0.0;
        string sparam = "";
        
        IndicatorSetString(INDICATOR_SHORTNAME, def_SHORTNAME);
        if(!GlobalVariableGet(def_GlobalVariableLeverage, dparam)) return INIT_FAILED;
        Terminal.Init();
        Chart.AddThese("IDE(,,170, 215)");
        Chart.InitilizeChartTrade(dparam * Terminal.GetVolumeMinimal(), GlobalVariableGet(def_GlobalVariableTake), GlobalVariableGet(def_GlobalVariableStop), true);
        OnChartEvent(CHARTEVENT_OBJECT_ENDEDIT, lparam, dparam, sparam);

        return INIT_SUCCEEDED;
}

A linha destacada irá testar uma das variáveis globais, no caso a que indica o nível de alavancagem, caso esta variável não esteja presente, a inicialização irá falhar. Lembre-se que o responsável por criar esta variável é o EA e não o Chart Trade, por conta disto, o indicador irá sabe se o EA está ou não presente no gráfico, e quando o EA sair, ele irá retirar esta variável do MetaTrader 5, forçando assim com que o Chart Trade também seja removido junto, e isto é feito em um segundo ponto, onde testamos esta mesma condição, da presença ou não da variável global de alavancagem.

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        if (!GlobalVariableCheck(def_GlobalVariableLeverage)) OnDeinit(REASON_INITFAILED);
        Chart.DispatchMessage(id, lparam, dparam, sparam);
}

Este ponto indicado acima, irá fazer o teste com uma frequência relativamente alta, apesar de ser tentador colocar um sistema de evento OnTime, em um indicador, isto não é aconselhável, já que todos os indicadores utilizam um mesmo thread de funcionamento, e colocar um evento OnTime em um indicador irá afetar todos os demais, se isto não for feito com extrema atenção.

Mas caso você ache, que este teste acima, esta sendo feito de forma a atrapalhar o desempenho geral da plataforma, você pode colocar este mesmo teste dentro de um evento OnTime, mas esteja ciente das consequências que isto irá trazer.

Independentemente de quem acione a retirada do indicador Chart Trade do gráfico, ela irá ocorrer no mesmo ponto, e este é visto logo a seguir:

void OnDeinit(const int reason) 
{ 
        if (reason == REASON_INITFAILED)
        {
                Print("Não é possivel usar o Chart Trade. EA não esta presente no gráfico deste ativo...");
                if (!ChartIndicatorDelete(0, 0, def_SHORTNAME))
                        Print("Não foi possivel remover o Chart Trade do Gráfico.");
        }
}

A linha em destaque irá remover o indicador do gráfico, mas caso ela não tenha êxito uma mensagem irá informar isto, estas mensagens podem ser vista na caixa de ferramentas conforme mostrado abaixo:

Desta forma, você deve ficar sempre atento a qualquer informação presente nesta janela.

Bom, mas falta uma última rotina antes de passarmos a uma analise mais profunda, sobre as mudanças feitas na classe C_Chart_IDE, esta rotina é vista logo abaixo:

int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        double value;
        
        if (GlobalVariableGet(def_GlobalVariableResult, value))
        {
                GlobalVariableDel(def_GlobalVariableResult);
                Chart.DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, value, C_Chart_IDE::szMsgIDE[C_Chart_IDE::eROOF_DIARY]);
        }
   
        return rates_total;
}

O que esta rotina faz é ficar observando as variáveis globais, e de tempos em tempos, quando uma posição é fechada, o EA irá criar uma variável global, cujo nome esta definido em def_GlobalVariableResult, quando esta variável for criada e o nome for o mesmo que o Chart Trade está observando, o valor desta será capturado, e a variável será imediatamente removida, isto para evitar que em caso de o EA enviar uma atualização antes do tratamento ter sido realmente efetivado, esta atualização seja perdida, mas com o valor capturado antes da remoção, podemos então envia-lo para dentro da classe do Chart Trade responsável por tratar as mensagens, assim o valor que o EA informar, será mostrado no Chart Trade o quanto antes.

Desta forma terminamos a parte inicial, para conseguir fazer com que o indicador de Chart Trade seja funcional. Mas ainda temos uma segunda parte: Os botões. Temos que fazer com que eles sejam funcionais. Isto é algo extremamente simples de ser conseguido, na rotina que trata das mensagens na classe C_Chart_IDE vemos o seguinte fragmento:

// ... Código anterior ...

case CHARTEVENT_OBJECT_CLICK:
        if (StringSubstr(sparam, 0, StringLen(def_HeaderMSG)) != def_HeaderMSG)
	{
		Resize(-1);
		return;
	}
	sparam = StringSubstr(sparam, 9, StringLen(sparam));
	StringToUpper(sparam);
#ifdef def_INTEGRATION_CHART_TRADER
	if ((sparam == szMsgIDE[eBTN_SELL]) || (sparam == szMsgIDE[eBTN_BUY]))
		TradeView.ExecuteOrderInMarket(m_BaseFinance.Leverange, m_BaseFinance.FinanceTake, m_BaseFinance.FinanceStop, sparam == szMsgIDE[eBTN_BUY], m_BaseFinance.IsDayTrade);
	if (sparam == szMsgIDE[eBTN_CANCEL])
	{
		TradeView.CloseAllsPosition();
		ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false);
	}
#else 
	{
		union u00
		{
			double Value;
			ulong c;
		}u_local;
                                                        
		u_local.c = 0;
		if (sparam == szMsgIDE[eBTN_BUY]) u_local.c = (m_BaseFinance.IsDayTrade ? def_ButtonDTSelect : def_ButtonSWSelect) + def_ButtonBuyMarket; else
		if (sparam == szMsgIDE[eBTN_SELL]) u_local.c = (m_BaseFinance.IsDayTrade ? def_ButtonDTSelect : def_ButtonSWSelect) + def_ButtonSellMarket; else
		if (sparam == szMsgIDE[eBTN_CANCEL])
		{
			u_local.c = def_ButtonClosePosition;
			ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false);
		}
                if (u_local.Value > 0) GlobalVariableSet(def_GlobalVariableButton, u_local.Value);
	}
#endif 
	if (sparam == szMsgIDE[eCHECK_DAYTRADE]) InitilizeChartTrade(0, 0, 0, m_BaseFinance.IsDayTrade ? false : true);
	break;

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

Observem que temos 2 código neste fragmento. Um em AZUL que é usado quando o Chart Trade esta embutido no EA, e outro em VERDE que é quando o Chart Trade esta presente como sendo um indicador.

O que nos interessa aqui é o código em VERDE. Este irá criar uma variável global e irá colocar nela o valor do status dos botões. Então caso o operador tenha fechado uma posição, o valor correspondente ao botão de fechamento, será colocado na variável, e somente este valor, mas caso ele esteja enviando uma ordem a mercado, este valor será diferente, ele agora será uma combinação de dois outros valores: Um que indicará se a ordem é de compra ou venda, e o outro se estamos querendo fazer Day Trade ou uma operação mais longa, é somente isto que o Chart Trade irá indicar para o EA.

IMPORTANTE: Este sistema é auto exclusivo, ou seja caso você  tenha clicado no botão de vender e antes do EA fazer qualquer coisa você clique no de compra, o EA irá de fato compra já que o valor indicando que era para vender irá ter sido perdido com a inclusão do valor indicando que era para comprar. E caso você peça para vender ou comprar, e já tenha uma posição aberta, e antes do EA fazer a compra ou venda, você clique em cancelar, a posição será fechada.

Feita esta consideração podemos passar para o código do EA para ver como ele irá funcionar neste novo modelo.


2.2 - Modificando o EA para aceitar as mensagens do indicador Chart Trade

Esta parte será bastante simples, já que tudo que teremos que fazer é ajustar alguns pequenos detalhes, no entanto lembre-se do seguinte: Uma vez que o EA e o Chart Trade estiverem sido carregados, não mude as variáveis globais, ou os dados contidos no EA, use o sistema de ordens para isto, ou o próprio Chart Trade, caso contrário você poderá ter problemas. Existem soluções para alguns problemas e mas não existe solução para outros, mas pelo sim ou pelo não, use as ferramentas que estão disponíveis, não tente complicar a sua vida como operador. 

No artigo anterior Desenvolvendo um EA de negociação do zero ( Parte 29 ), ao promover a remoção do Chart Trade, fizemos alguns alterações, e parte delas irão ser desfeitas, não iremos precisar mexer em mais nada neste tópico, mas como foi dito acima, existem coisas que tem solução e outras que não, desta forma no próximo tópico deste artigo, vamos corrigir alguns pequenos problemas nesta relação, entre o Chart Trade e o EA.

Mas vamos ver o que teremos de desfazer e acionar no EA, para que exista algum nível de comunicação entre ele e o Chart Trade.

Primeiramente vamos modificar as seguintes entradas:

input int       user20      = 1;        //Fator de alavancagem
input double    user21      = 100;      //Take Profit ( FINANCEIRO )
input double    user22      = 81.74;    //Stop Loss ( FINANCEIRO )
input bool      EA_user23   = true;     //Day Trade ?

O valor que indica se o EA, irá preferencialmente abrir uma posição longa ou curta, não será modificado, isto deverá ser feito no Chart Trade, ou em uma ordem pendente, que você irá colocar no gráfico, mas já mostrei como fazer isto em artigos anteriores, desta maneira vamos continuar o trabalho de codificação. No evento OnInit, precisamos das seguintes modificações, neste primeiro momento:

int OnInit()
{
        if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
        {
                Sound.PlayAlert(C_Sounds::TRADE_ALLOWED);
                return INIT_FAILED;
        }
        
        Terminal.Init();

#ifdef def_INTEGRATION_TAPE_READING
        VolumeAtPrice.Init(user32, user33, user30, user31);
        TimesAndTrade.Init(user41);
        EventSetTimer(1);
#endif 

        Mouse.Init(user50, user51, user52);
        
#ifdef def_INTEGRATION_CHART_TRADER
        static string   memSzUser01 = "";
        if (memSzUser01 != user01)
        {
                Chart.ClearTemplateChart();
                Chart.AddThese(memSzUser01 = user01);
        }
        Chart.InitilizeChartTrade(EA_user20 * Terminal.GetVolumeMinimal(), EA_user21, EA_user22, EA_user23);
        TradeView.Initilize();
   OnTrade();
#else
        GlobalVariableTemp(def_GlobalVariableLeverage);
        GlobalVariableTemp(def_GlobalVariableTake);
        GlobalVariableTemp(def_GlobalVariableStop);
        GlobalVariableTemp(def_GlobalVariableResult);
        GlobalVariableSet(def_GlobalVariableLeverage, user20 * Terminal.GetVolumeMinimal());
        GlobalVariableSet(def_GlobalVariableTake, user21);
        GlobalVariableSet(def_GlobalVariableStop, user22);
        TradeView.Initilize();
        GlobalVariableSet(def_GlobalVariableResult, TradeView.GetFinanceRoof());
#endif 
   
        return INIT_SUCCEEDED;
}

Vejam que aqui adicionamos as variáveis globais, que serão usadas na comunicação, como eu havia mencionando antes, o EA deverá entrar sempre antes do Chart Trade, caso contrário teremos o impedimento da inicialização do indicador. Observem que os valores a serem usado incialmente pelo Chart Trade estão no EA, e a inicialização é completa, incluindo se já havia ocorrido alguma operação anterior, o valor acumulado também é repassado para o Chart Trade.

Um detalhe importante, notem que as variáveis criadas são do tipo temporárias, isto por que não quero de forma alguma, que caso venhamos a fazer um despejo de dados do EA, estas variáveis sejam armazenadas, pois elas não tem utilidade depois de um certo período de tempo, e mesmo que algo venha a acontecer, e a plataforma seja encerrada, estas variáveis irão deixar de existir.

A próxima adição que teremos que fazer é vista logo abaixo:

void OnDeinit(const int reason)
{
        Mouse.Destroy();
        TradeView.Finish();
#ifndef def_INTEGRATION_CHART_TRADER
        GlobalVariableDel(def_GlobalVariableLeverage);
        GlobalVariableDel(def_GlobalVariableTake);
        GlobalVariableDel(def_GlobalVariableStop);
        GlobalVariableDel(def_GlobalVariableResult);
        GlobalVariableDel(def_GlobalVariableButton);
#endif
#ifdef def_INTEGRATION_TAPE_READING
        EventKillTimer();
#endif 
}

Mesmo que as variáveis sejam temporárias, ainda assim peço para que o EA as remova forçosamente, isto para garantir que o Chart Trade também deixará de permanecer no gráfico. Existe um probleminha neste evento OnDeinit, mas iriei falar isto no próximo tópico, então vamos ver um outro ponto, este sim é bastante curioso. Você pode fazer de 2 formas diferentes, e os resultados serão QUASE, e temos que enfatizar esta palavra QUASE idênticos, pois existem pequenas diferenças que podem fazer a diferença. ( Me desculpem pelo trocadilho, mas não pude resistir .... 😂👍)

Lembram que no Chart Trade existem alguns botões, e estes enviam mensagens para o EA por meio de uma variável global ? Pois bem, a rotina dentro do EA que responde de forma adequada aqueles cliques é vista logo abaixo:

inline void ChartTrade_ClickButton(void)
{
        union u00
        {
                double Value;
                ulong c;
        }u_local;
        
        if (GlobalVariableGet(def_GlobalVariableButton, u_local.Value))
        {
                GlobalVariableDel(def_GlobalVariableButton);
                                
                if (u_local.c == def_ButtonClosePosition) TradeView.CloseAllsPosition(); else
                        TradeView.ExecuteOrderInMarket(GlobalVariableGet(def_GlobalVariableLeverage), GlobalVariableGet(def_GlobalVariableTake), GlobalVariableGet(def_GlobalVariableStop), ((u_local.c & def_ButtonBuyMarket) == def_ButtonBuyMarket), ((u_local.c & def_ButtonDTSelect) == def_ButtonDTSelect));
                TradeView.Initilize();
        }
}

Esta rotina esta declarada como sendo uma rotina inline, ou seja ela deverá ser colocada pelo compilador na posição onde for declarada, isto fará com que ela seja executada de forma o mais rápido, quanto for possível.

O grande detalhe é: Onde colocar esta rotina ?!?! Vamos pensar um pouco ... Observem que temos um teste nela, então ela não será executada o tempo todo, desta forma temos 2 possibilidades. A primeira é colocar ela dentro do evento OnTick, a segunda é colocar ela dentro do evento OnTime. Esta escolha tem que ser feita baseadas em algum tipo de lógica, caso contrário poderemos ter problemas aqui.

Então pensemos: Se você colocar esta rotina dentro do evento OnTick ela será executada a cada novo tick, que for recebido pela plataforma, vindos do servidor de negociação, isto parece ser bom e adequado, já que esta rotina não irá ser executada muitas vezes, apenas em alguns pouco momentos, mas isto nos traz um problema, caso a volatilidade do ativo, que estejamos negociando, seja muito baixa, a taxa de eventos OnTick será igualmente muito baixa, ou seja, podemos ter problemas com relação ao momento de disparo, do evento por meio do clique no Chart Trade.

Então vamos pensar na segunda opção: Colocar a rotina no evento OnTime, o fato de fazermos isto, irá nos dar uma boa segurança, com relação ao ponto que esta rotina será executada, dado que o evento OnTime, será disparado com uma certa regularidade, mas ao fazermos isto, teremos que ter em mente, que não poderemos mais usar o evento OnTime para nenhuma outra coisa, que não seja observar as variáveis globais.

Neste ponto, temos uma escolha bastante adequada, já que o EA irá ficar apenas por conta de observar o mercado, e no decorrer dos próximos artigos vocês verão que isto irá ficar cada vez mais evidente, é então uma boa escolha colocar a rotina dentro do evento OnTime, mas agora vem um questão: O evento OnTime é disparado apenas a cada segundo, certo ?

Na verdade é mais comum que ele dispare a cada segundo, mas podemos colocar um tempo menor, para isto usaremos uma função um pouco diferente: EventSetMillisecondTimer, desta forma podemos disparar o evento em um tempo inferior a 1 segundo. Acredito que 500 ms seja o suficiente para uma ampla gama de casos, então no evento OnInit do EA teremos que acionar a seguinte linha:

#else
        GlobalVariableTemp(def_GlobalVariableLeverage);
        GlobalVariableTemp(def_GlobalVariableTake);
        GlobalVariableTemp(def_GlobalVariableStop);
        GlobalVariableTemp(def_GlobalVariableResult);
        GlobalVariableSet(def_GlobalVariableLeverage, user20 * Terminal.GetVolumeMinimal());
        GlobalVariableSet(def_GlobalVariableTake, user21);
        GlobalVariableSet(def_GlobalVariableStop, user22);
        TradeView.Initilize();
        GlobalVariableSet(def_GlobalVariableResult, TradeView.GetFinanceRoof());
        EventSetMillisecondTimer(500);
#endif 

E não podemos esquecer de também encerrar este evento na rotina OnDeinit, para isto bastará adiciona a seguinte linha no evento:

void OnDeinit(const int reason)
{
        EventKillTimer();

Agora vamos ver como ficou o evento OnTime, que nada mais é que um código super simples, onde apenas a linha em destaque abaixo será de fato compilada.

void OnTimer()
{
#ifndef def_INTEGRATION_CHART_TRADER
        ChartTrade_ClickButton();
#endif

#ifdef def_INTEGRATION_TAPE_READING
        VolumeAtPrice.Update();
        TimesAndTrade.Connect();
#endif 
}

Mas é somente isto ?!?! Não .... temos que resolver um outro probleminha, lembra que mudamos as variáveis de inicialização do EA, e queremos puxar os dados do Chart Trade. todas as vezes que uma nova ordem pendente vier a ser colocada no gráfico, pois bem para fazer isto temos que mudar um detalhe no código da classe C_IndicatorTradeView, e este é visto logo abaixo no fragmento a seguir:

        case CHARTEVENT_MOUSE_MOVE:
                Mouse.GetPositionDP(dt, price);
                mKeys   = Mouse.GetButtonStatus();
                bEClick  = (mKeys & 0x01) == 0x01;    //Clique esquerdo
                bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT Pressionada
                bKeySell = (mKeys & 0x08) == 0x08;    //CTRL Pressionada
                if (bKeyBuy != bKeySell)
                {
                        if (!bMounting)
                        {
#ifdef def_INTEGRATION_CHART_TRADER
                                m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl);
#else 
                                m_Selection.vol = GlobalVariableGet(def_GlobalVariableLeverage) * Terminal.GetVolumeMinimal();
                                valueTp = GlobalVariableGet(def_GlobalVariableTake);
                                valueSl = GlobalVariableGet(def_GlobalVariableStop);
                                m_Selection.bIsDayTrade = EA_user23;
#endif 

Agora os códigos em destaque estão capturando os valores que estão nas variáveis globais, desta forma o que estiver no Chart Trade será colocado no sistema de ordens, de forma que você possa posicionar elas com o mínimo de trabalho, o único detalhe, é com relação ao fato que todas as ordens pendentes seguirão o tempo que o EA estará especificando, mas isto pode ser mudado diretamente no gráfico, para mais detalhes veja: Desenvolvendo um EA de negociação do Zero ( Parte 27 ), lá foi mostrado como fazer para modificar as ordens pendentes, diretamente no gráfico, sem ter que passar pelo Chart Trade.

Existe uma pequena modificação no sistema de som, onde foram adicionados sons na classe C_Router, isto para garantir que os avisos de entrada e fechamento de posição sejam de fato feitos, no caso de você venha a fazer a operação via Chart Trade. É algo simples, então estou somente mencionando, caso você queira retirar ou modificar algo no sistema de som, fique ciente que terá que observar em outros pontos também.

Antes de terminarmos, foi dito que o evento OnDeinit tem um problema, que iremos corrigir, então vamos para o próximo tópico, sanar este problema.


2.3 - Evitando que o Chart Trade saia do gráfico prematuramente.

Quando fazemos qualquer coisa, seja mudança no tempo gráfico, seja mudanças nos parâmetros do EA, não importa, qualquer coisa, o MetaTrader 5 irá gerar um evento, o nome deste evento é OnDeint, o fato deste evento ser disparando, faz com que todas as coisas precisem ser reanalisadas, para que tudo continue funcionado de forma como esperado.

Na maior parte das vezes, o disparo deste evento não nos gera nenhum problema, mas a partir do momento em que estamos fazendo um sistema cliente - servidor, e usarmos variáveis globais para saber se o servidor deixou de ser executado, no caso o servidor é o EA, teremos que entender como contornar certas situações.

A função que trata o evento OnDeinit original tem o seguinte código:

void OnDeinit(const int reason)
{
        EventKillTimer();
        Mouse.Destroy();
        TradeView.Finish();
#ifndef def_INTEGRATION_CHART_TRADER
        GlobalVariableDel(def_GlobalVariableLeverage);
        GlobalVariableDel(def_GlobalVariableTake);
        GlobalVariableDel(def_GlobalVariableStop);
        GlobalVariableDel(def_GlobalVariableResult);
        GlobalVariableDel(def_GlobalVariableButton);
#endif
#ifdef def_INTEGRATION_TAPE_READING
        EventKillTimer();
#endif 
}

A linha em destaque irá remover uma variável global, que é justamente a que usamos no indicador Chart Trade para verificar se o EA esta ou não no gráfico, e esta verificação é feita no código abaixo:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        if (!GlobalVariableCheck(def_GlobalVariableLeverage)) OnDeinit(REASON_INITFAILED);
        Chart.DispatchMessage(id, lparam, dparam, sparam);
}

Ou seja, toda a vez que alguma coisa acontecer ao EA, e o evento OnDeinit for disparado a tal variável será removida, e antes que o EA de fato volte a criar a variável, o indicador já poderá ter sido removido do gráfico, estes poderá é realmente muito atípico, já que em alguns momentos isto irá acontecer, e em outros não, mas não podemos ficar a mercê da sorte, temos que de alguma forma garantir que as coisas funcionem conforme é esperado.

Então observando a documentação, encontrei a solução para o problema, e esta pode ser vista em: Códigos de Motivos de Desinicialização, uma vez observando estes código, podemos ajustar a rotina de tratamento do evento OnDeinit, de forma que as variáveis não sejam removidas, a menos que de fato o EA será removido do gráfico.

Então a solução, e o novo código de tratamento ficou conforme mostrado abaixo:

void OnDeinit(const int reason)
{
        EventKillTimer();
        Mouse.Destroy();
        TradeView.Finish();
#ifndef def_INTEGRATION_CHART_TRADER
        switch (reason)
        {
                case REASON_CHARTCHANGE:
                        break;
                default:                
                        GlobalVariableDel(def_GlobalVariableLeverage);
                        GlobalVariableDel(def_GlobalVariableTake);
                        GlobalVariableDel(def_GlobalVariableStop);
                        GlobalVariableDel(def_GlobalVariableResult);
                        GlobalVariableDel(def_GlobalVariableButton);
        };
#endif
#ifdef def_INTEGRATION_TAPE_READING
        EventKillTimer();
#endif 
}

Ou seja, agora caso estejamos apenas mudando o período gráfico, ou a forma como o gráfico é plotado, não teremos mais o inconveniente de que o indicador Chart Trade saia do gráfico, em qualquer outra situação ele poderá sair do gráfico, já que não temos a real segurança de que dará tudo certo, por conta disto uma vez que o EA e o Chart Trade tenham sido carregados, não fará mais sentido modificar os parâmetros do EA.


3.0- Conclusão

Virão o que um pouco de criatividade pode fazer ? As vezes temos que solucionar alguns problemas que parecem insolúveis.  Mas estudando a documentação, podemos encontrar a solução, então é primordial sempre observar a documentação, entende-la  e testar as coisas, para que nossas ideias sejam de fato colocadas em prática.


Arquivos anexados |
EA_-_p_Parte_30_u.zip (14532.23 KB)
Funcionalidades do assistente MQL5 que você precisa conhecer (Parte 1): Análise de regressão Funcionalidades do assistente MQL5 que você precisa conhecer (Parte 1): Análise de regressão
O trader moderno está quase sempre procurando novas ideias, consciente ou inconscientemente. Ele está constantemente tentando novas estratégias, modificando-as e descartando aquelas que não funcionam. Este processo de pesquisa é demorado e propenso a erros. Nesta série de artigos, tentarei provar que o assistente MQL5 é um verdadeiro suporte para qualquer operador. Graças ao assistente, o trader economiza tempo ao implementar suas ideias. Também reduz a probabilidade de erros que ocorrem ao duplicar o código. Assim, em vez de perder tempo com codificação, os operadores colocam em prática sua filosofia de negociação.
Como desenvolver um sistema de negociação baseado no indicador Acumulação/Distribuição (AD) Como desenvolver um sistema de negociação baseado no indicador Acumulação/Distribuição (AD)
Bem-vindo ao novo artigo da nossa série sobre como aprender a projetar sistemas de negociação com base nos indicadores técnicos mais populares. Neste artigo, nós aprenderemos sobre um novo indicador técnico chamado Acumulação/Distribuição e descobriremos como desenvolver um sistema de negociação em MQL5 baseado nas estratégias simples com o AD.
Ciência de Dados e Aprendizado de Máquina (Parte 04): Previsão de um crash no mercado de ações Ciência de Dados e Aprendizado de Máquina (Parte 04): Previsão de um crash no mercado de ações
Neste artigo, eu tentarei usar nosso modelo logístico para prever o crash do mercado de ações com base nos fundamentos da economia dos EUA, nos concentraremos nas ações do NETFLIX e da APPLE, usando os crashes anteriores do mercado de 2019 e 2020, vamos ver como nosso modelo se comportará nas atuais desgraças e tristezas.
Experiências com redes neurais (Parte 1): Lembrando a geometria Experiências com redes neurais (Parte 1): Lembrando a geometria
As redes neurais são tudo para nós. Vamos ver se isso é verdade na prática. Para tal, vamos fazer experiências e adotar abordagens não-convencionais. Vamos escrever também um sistema de negociação lucrativo. A explicação vai ser simples.