English Русский 中文 Español Deutsch 日本語
preview
Aprendendo a construindo um Expert Advisor que opera de forma automática (Parte 15): Automação (VII)

Aprendendo a construindo um Expert Advisor que opera de forma automática (Parte 15): Automação (VII)

MetaTrader 5Exemplos | 16 fevereiro 2023, 14:08
1 119 6
Daniel Jose
Daniel Jose

Introdução

No artigo anterior,  Aprendendo a construindo um EA que opera de forma automática (Parte 14):  Automação (VI), falei sobre a classe C_Automaton, expliquei o básico sobre ela. Mas já que fazer o uso da classe C_Automaton, de modo a automatizar o sistema, não é algo assim tão simples, como pode parecer, iremos ver neste artigo, com uso de exemplos, como fazer isto.

Aqui veremos 3 modelos distintos, a toda a explicação irá focar, única e exclusivamente, na explicação de como adaptar a classe C_Automaton, a fim de que cada um dos modelos seja implementado. Se desejar saber mais detalhes sobre o sistema, veja os artigos anteriores. Pois aqui será somente a parte da adaptação, ou seja, a parte dependente.

Então sem mais delongas, já que o tema é de fato bem longo. Vamos partir para os exemplos práticos. 


Primeiro exemplo de automação: Usando a média móvel exponencial de 9

Este exemplo, usa o código do EA, que você encontrará no anexo como sendo EA_v1.mq5. Vamos começar, com o constructor da classe, este pode ser visto no fragmento abaixo:

                C_Automaton(const ulong magic, double FinanceStop, double FinanceTake, uint Leverage,
                            bool IsDayTrade, double Trailing, const ENUM_TIMEFRAMES iPeriod,
                            const double OverBought = 70, const double OverSold = 30, const int iShift = 1)
                        :C_Manager(magic, 0, 0, Leverage, IsDayTrade, 0, false, 10),
                         m_TF(iPeriod),
                         m_Handle(INVALID_HANDLE)
                        {
                                m_Infos.Shift      = iShift;
                                m_Infos.OverBought = OverBought;
                                m_Infos.OverSold   = OverSold;
                                ArraySetAsSeries(m_Buff, true);
                                m_nBars  = iBars(NULL, m_TF);
                                m_Handle = iMA(NULL, m_TF, 9, 0, MODE_EMA, PRICE_CLOSE);
                        }

Vamos entender o que temos aqui, diferente do sistema default:

  • Não teremos, stop loss ou take profit no sistema, ou seja, não haverá um limite de perda ou ganho no sistema, o próprio EA, irá gerar estes limites, dependendo dos movimentos do preço;
  • Não é para ser criado nenhum tipo de trailing stop, breakeven ou ordem pendente de stop;
  • Iremos utilizar apenas um handle, e este será um indicador de média móvel exponencial de 9 períodos, no preço de fechamento.

Todo o restante, já foi explicado anteriormente, então podemos passar, para o procedimento de cálculo deste mecanismo. A forma de se fazer isto, foi explicado no artigo anterior, veja o mesmo para mais detalhes. Mas uma vez definido, a forma de calcular, eis que surge o seguinte código:

inline eTrigger CheckTrigger(void)
                        {
                                int iRet;
                                        
                                if (((iRet = iBars(NULL, m_TF)) > m_nBars) && (m_Handle != INVALID_HANDLE))
                                {
                                        if (CopyBuffer(m_Handle, 0, 0, m_Infos.Shift + 1, m_Buff) < m_Infos.Shift + 1) return TRIGGER_NONE;
                                        m_nBars = iRet;
                                        if (m_Buff[0] > m_Buff[m_Infos.Shift]) return TRIGGER_BUY;
                                        if (m_Buff[0] < m_Buff[m_Infos.Shift]) return TRIGGER_SELL;
                                };
                                return TRIGGER_NONE;
                        }

Parece complicado ?!?! Bem, mas não é complicado de forma alguma. Muito pelo contrário, é bastante simples de ser feito, desde que você tenha criado as regras, em um fluxograma, conforme expliquei no artigo anterior. A primeira coisa que fazemos, é tentar ler o conteúdo do indicador, se isto não for possível, retornaremos um disparo nulo, e o sinal pode vim a ser perdido.

Em alguns casos, principalmente se o sinal era um de saída, o sistema pode começar a gerar perdas, mas não podemos, de maneira alguma fazer suposições. Por este motivo, que o EA, deverá sempre ser supervisionado. Uma possível solução, seria fazer com que um sinal de fechamento, de posição, fosse enviado para a classe C_Manager, por conta de que algum erro, que possa ter acontecido, mas como disse: Não podemos fazer suposições, então fica a seu critério, adicionar ou não, este sinal a fim de fechar a posição.

Atualizamos o contador de barras, para que o sinal seja disparado, somente na próxima barra. Mas isto somente acontecerá de fato, se houve uma resposta do indicador, caso contrário, na próximo evento OnTime, teremos novamente o teste do sinal, por isto, não devemos de fato fazer suposições, sobre o que está ocorrendo. Notem a sequencia com que os fatos vão acontecendo, se a atualização do numero de barras, tivesse ocorrido antes, iriamos perder o próximo evento OnTime, tendo que esperar, a próxima barra ser formada. Mas como atualizamos depois, podemos receber um evento OnTime, e assim tentar ler novamente o indicador.

Agora fazemos de fato o calculo, a fim de determinar se iremos comprar ou vender. Para entender este cálculo, você precisa entender, como a média móvel exponencial é calculada. Diferente de outras médias móveis, a média exponencial, é consideravelmente mais rápida. Então assim que ela inclinar, por conta do preço de fechamento, que definimos lá no constructor, ela estará na verdade, indicando se a barra, fechou acima ou abaixo dela.

Mas existe um detalhe neste cálculo, ele somente irá informar isto, com rapidez suficiente, se estivermos comparando o valor atual da média, com o valor imediatamente anterior, ou seja, qualquer mínima mudança, fará com que o disparo aconteça. Caso você deseje reduzir o nível de sensibilidade, deverá mudar o valor contido na variável m_Infos.Shift, de 1 para 2, ou um valor ainda maior. Com isto, obtemos uma simulação de deslocamento da média móvel, a fim de capturar um determinado tipo de movimento, reduzindo ou aumentando a sensibilidade do sistema.

Este tipo de deslocamento, é bem comum em alguns setups, como por exemplo o conhecido Joe di Napoli, onde fazemos uso de uma média móvel deslocada, muitos acham ser necessário observar a barra em relação a média móvel. Mas isto se deve ao fato, de que tais pessoas, não estudaram o modelo a fundo, pois se o fizer, verá que bastará deslocar a média, em um valor adequado, que você irá conseguir saber, se a barra está fazendo o não o padrão. Se bem que no caso do setup, Joe di Napoli, é necessário, que você faça um calculo de zig-zag na média móvel, a fim de que o disparo, seja feito no ponto adequado. Mas ainda assim, não precisamos observar as barras, apenas olhando a média será o suficiente.

Um detalhe a respeito do calculo acima: O ponto zero, indica o valor mais recente do buffer, ou seja o último valor calculado pelo indicador. 

Então, caso o valor mais recente, seja maior, que o valor mais antigo, indica que o EA, deverá comprar imediatamente. Se o valor for menor, que o valor mais antigo, o EA deverá vender imediatamente.

Aqui mora uma questão curiosa: Se você olhar, irá notar que este sistema, se parece com o muito conhecido sistema Larry Willians. Mas muitas pessoas, fazem uso do seguinte ponto, que pode ser adicionado ao calculo se você assim desejar, ao invés de comprar ou vender imediatamente, você pode mandar uma ordem pendente, para a máxima ou mínima da barra, que fez a média móvel disparar o sinal. Como a classe C_Manager, irá garantir, que apenas uma ordem pendente, fique no servidor, você teria apenas que mudar, não o calculo, mas sim a função Triggers, de maneira que no lugar de um requerimento de operação a mercado, o sistema enviasse uma ordem pendente, com os dados da barra que gerou o sinal.

Não será este o código que estará no anexo, mas a coisa ficaria conforme mostrado no fragmento abaixo:

inline virtual void Triggers(void) final
                        {
                                if (!CtrlTimeIsPassed()) ClosePosition(); else switch (CheckTrigger())
                                {
                                        case TRIGGER_BUY:
                                                if (m_Memory == TRIGGER_SELL) ClosePosition();
                                                if (GetVolumeInPosition() == 0)
                                                {
                                                        DestroyOrderPendent();
                                                        CreateOrder(ORDER_TYPE_BUY, iHigh(NULL, m_TF, 0));
                                                }
                                                m_Memory = TRIGGER_BUY;
                                                break;
                                        case TRIGGER_SELL:
                                                if (m_Memory == TRIGGER_BUY) ClosePosition();
                                                if (GetVolumeInPosition() == 0)
                                                {
                                                        DestroyOrderPendent();
                                                        CreateOrder(ORDER_TYPE_SELL, iLow(NULL, m_TF, 0));
                                                }
                                                m_Memory = TRIGGER_SELL;
                                                break;
                                }
                        };

Esta função foi adicionado, no código da classe C_Manager, e estará no anexo, não se preocupe com isto. Agora observe como a ordem estará sendo criada, diferente de uma entrada a mercado. Agora temos uma entrada baseada no preço, de um dos limites da barra, e este irá mudando, conforme as coisas irão acontecendo, de forma que se na barra, onde o disparo foi criado, a ordem não foi executada. No próximo disparo, a ordem será reposicionada. de maneira automática, mas este modelo, não estará de fato no anexo, estou apenas demonstrando o que se pode fazer.

Acredito que você, já tenha conseguido entender o nível de flexibilidade do sistema. Mas ainda se quer arranhei a superfície, do que se pode fazer com este sistema de classe. Para demonstrar um outro tipo de uso, vamos ver um outro exemplo.


Segundo exemplo de automação: Usando o indicador RSI ou IFR

Agora que você já viu o uso de um sistema de média móvel, vamos ver um uso com indicador. Neste caso, iremos usar apenas um indicador que muitos gostam. Mas o método funciona para qualquer outro indicador, no caso oscilador, mas a ideia é a mesma em qualquer caso. O código do EA neste caso,

será visto no anexo como EA_v2.mq5, então vamos começar no constructor, que pode ser visto logo abaixo:

                C_Automaton(const ulong magic, double FinanceStop, double FinanceTake, uint Leverage,
                            bool IsDayTrade, double Trailing, const ENUM_TIMEFRAMES iPeriod,
                            const double OverBought = 70, const double OverSold = 30, const int iShift = 1)
                        :C_Manager(magic, FinanceStop, 0, Leverage, IsDayTrade, Trailing, true, 10),
                         m_TF(iPeriod),
                         m_Handle(INVALID_HANDLE)
                        {
                                m_Infos.Shift      = iShift;
                                m_Infos.OverBought = OverBought;
                                m_Infos.OverSold   = OverSold;
                                ArraySetAsSeries(m_Buff, true);
                                m_nBars = iBars(NULL, m_TF);
                                m_Handle = iRSI(NULL, m_TF, 14, PRICE_CLOSE);

                        }

Diferente do que aconteceu no exemplo anterior, vamos ver o que temos aqui. Lembrando que você pode fazer a mesma coisa do exemplo anterior, ou tornar o exemplo anterior igual a este:

  • Aqui já definimos um valor a ser usado como Stop loss da operação, este de fato será criado assim que a posição for aberta;
  • Informamos um valor que será usado como Breakeven, Trailing Stop;
  • Dizemos que iremos usar uma ordem pendente como ponto de stop;
  • Indicamos que iremos usar o indicador RSI com o período definido em 14, e baseado no preço de fechamento.
  • Os valores de sobrecomprado e sobrevendido são informados aqui, e armazenados para uso posterior.

Notaram como é super simples, você pode simplesmente, utilizar um outro indicador, sem nenhum tipo de problema, bastará trocar o iRSI, por qualquer outro que você deseja. A próxima coisa a ser feita, será a parte do calculo, esta pode ser vista abaixo:

inline eTrigger CheckTrigger(void)
                        {
                                int iRet;
                                        
                                if (((iRet = iBars(NULL, m_TF)) > m_nBars) && (m_Handle != INVALID_HANDLE))
                                {
                                        if (CopyBuffer(m_Handle, 0, 0, m_Infos.Shift + 1, m_Buff) < m_Infos.Shift + 1) return TRIGGER_NONE;
                                        m_nBars = iRet;
                                        if ((m_Buff[0] > m_Buff[m_Infos.Shift]) && (m_Buff[0] > m_Infos.OverSold) && (m_Buff[m_Infos.Shift] < m_Infos.OverSold)) return TRIGGER_BUY;
                                        if ((m_Buff[0] < m_Buff[m_Infos.Shift]) && (m_Buff[0] < m_Infos.OverBought) && (m_Buff[m_Infos.Shift] > m_Infos.OverBought)) return TRIGGER_SELL;
                                };
                                return TRIGGER_NONE;
                        }

Aqui no calculo, seguimos fazendo alguns testes muito parecidos com os visto no exemplo anterior, com alguns detalhes extras. É bem verdade, mas ainda assim, você pode notar o seguinte: Estamos olhando se o movimento no indicador, se tornou descendente ou ascendente. Isto é importante já que pode ser, que o indicador esteja mostrando uma correção, dai o fato de termos um deslocamento, a fim de notar este tipo de movimentação. Mas de uma forma ou de outra, verificamos se o indicador, está mostrando sobrevenda ou sobrecompra, antes de tormamos uma segunda decisão, com base no fato de ele esteja saindo da região de sobrecompra ou sobrevenda. O que nos daria um gatilho de compra ou de venda.

No demais, não temos nenhuma grande mudança, como podem notar, a coisa em si é bastante simples, e não muda muito entre um tipo de estudo e outro. Bastando que informemos, qual o gatilho que irá fazer o disparo, poderemos nos adequar a qualquer modelo, e metodologia necessária para que o EA seja automatizado.

Mas diferente do exemplo anterior. Neste queremos fazer, com que tenhamos um movimento de breakeven e trailing stop. A fim de que caso aconteça algum tipo movimento em que possamos ter algum lucro, consigamos sair da posição, de uma maneira um pouco melhor. Isto é promovido fazendo a seguinte adição ao código:

inline virtual void Triggers(void) final
                        {
                                if (!CtrlTimeIsPassed()) ClosePosition(); else switch (CheckTrigger())
                                {
                                        case TRIGGER_BUY:
                                                if (m_Memory == TRIGGER_SELL) ClosePosition();
                                                if (m_Memory != TRIGGER_BUY) ToMarket(ORDER_TYPE_BUY);
                                                m_Memory = TRIGGER_BUY;
                                                break;
                                        case TRIGGER_SELL:
                                                if (m_Memory == TRIGGER_BUY) ClosePosition();
                                                if (m_Memory != TRIGGER_SELL) ToMarket(ORDER_TYPE_SELL);
                                                m_Memory = TRIGGER_SELL;
                                                break;
                                }
                                TriggerTrailingStop();
                        };

Veja que praticamente toda a rotina se manteve igual a original. Mas foi adicionada esta chamada, que irá promover o que queremos, no caso o breakeven e o trailing stop. Notem que tudo é uma questão de quando, e onde colocar as chamadas, já que o sistema, já contará com todos os detalhes a serem utilizados, se adequando a nossa necessidade, em cada um dos casos que desejamos de fato trabalhar.

Então, vamos ver mais um exemplo, para fixar melhor as ideias.


Terceiro exemplo de automação: Cruzamento de Médias Móveis

Este exemplo, pode ser visto ao se usar o EA_v3.mq5. Mas como não vamos ficar apenas no processo mais básico de automação. Você poderá ver, que na classe C_Manager, ocorram algumas pequenas modificações. A primeira delas, é o surgimento de uma rotina, para que o sistema de automação, saiba se existe uma posição comprada ou vendida. Esta é vista abaixo:

const bool IsBuyPosition(void) const
                        {
                                return m_Position.IsBuy;
                        }

Esta função, de fato não atesta se a posição esta ou não aberta, mas somente retorna se a variável esta indicando compra ou venda. A ideia não é de fato verificar, se existe uma posição aberta, e assim retornar se ela é comprada ou vendida, dai a simplicidade da função. Mas você pode testar se existe uma posição aberta, caso o seu sistema de automação, venha a exigir este tipo de verificação, mas de qualquer forma, para o que irei exemplificar, esta função já é o suficiente. Isto por conta da função que vem longo a seguir:

                void LockStopInPrice(const double Price)
                        {
                                if (m_InfosManager.IsOrderFinish)
                                {
                                        if (m_Pending.Ticket == 0) return;
                                        if ((m_Pending.PriceOpen > Price) && (m_Position.IsBuy)) return;
                                        if ((m_Pending.PriceOpen < Price) && (!m_Position.IsBuy)) return;
                                        ModifyPricePoints(m_Pending.Ticket, m_Pending.PriceOpen = Price, m_Pending.SL = 0, m_Pending.TP = 0);
                                }else
                                {
                                        if (m_Position.SL == 0) return;
                                        if ((m_Position.SL > Price) && (m_Position.IsBuy)) return;
                                        if ((m_Position.SL < Price) && (!m_Position.IsBuy)) return;
                                        ModifyPricePoints(m_Position.Ticket, m_Position.PriceOpen, Price, m_Position.TP);
                                }
                        }

O que esta função acima faz, é justamente posicionar o preço de Stop em um dado ponto. De certa forma, ela faz justamente o que é visto no código do trailing stop, quando já temos tido acionado um breakeven. Mas por conta do fato, que em alguns tipos de operacionais, não fazemos de fato um breakeven da posição. Mas sim movemos o stop loss, baseados em algo, tipo a máxima ou mínima da barra anterior, preço indicado em alguma média móvel, ou qualquer outro tipo de mecanismo de automação, precisamos de fato de uma função, apenas para fazer isto. Observem que ela tem mecanismos, que impedem de que o valor se desloque em uma direção, a fim de aumentar o prejuízo. Este tipo de trava é bem importante, caso você deseje enviar valores baseados em médias móveis.

Com base nisto, temos agora o seguinte código para a função de trailing stop:

inline void TriggerTrailingStop(void)
                        {
                                double price, v1;
                                
                                if ((m_Position.Ticket == 0) || (m_InfosManager.IsOrderFinish ? m_Pending.Ticket == 0 : m_Position.SL == 0)) return;
                                if (m_Position.EnableBreakEven) TriggerBreakeven(); else
                                {
                                        price = SymbolInfoDouble(_Symbol, (GetTerminalInfos().ChartMode == SYMBOL_CHART_MODE_LAST ? SYMBOL_LAST : (m_Position.IsBuy ? SYMBOL_ASK : SYMBOL_BID)));
                                        v1 = (m_InfosManager.IsOrderFinish ? m_Pending.PriceOpen : m_Position.SL);
                                        if (v1 > 0) if (MathAbs(price - v1) >= (m_Position.Gap * 2)) 
                                                LockStopInPrice(v1 + (m_Position.Gap * (m_Position.IsBuy ? 1 : -1)));
                                        {                                               
                                                price = v1 + (m_Position.Gap * (m_Position.IsBuy ? 1 : -1));
                                                if (m_InfosManager.IsOrderFinish) ModifyPricePoints(m_Pending.Ticket, m_Pending.PriceOpen = price, m_Pending.SL = 0, m_Pending.TP = 0);
                                                else    ModifyPricePoints(m_Position.Ticket, m_Position.PriceOpen, price, m_Position.TP);
                                        }
                                }
                        }

As partes riscadas, foram removidas por conta que agora temos a chamada, que se encontra em destaque. Desta forma podemos fazer uma melhor reutilização do código.

Agora que apresentei as modificações da classe C_Manager, vamos ver como de fato produzir este terceiro exemplo, começando com as mudanças. Estas podem ser diferentes de cada caso em si. Mas no entanto, você sempre deve se atentar ao planejamento feito, a fim de criar a automatização. E como a automatização aqui precisa de um pouco mais de coisas, do que nos casos anteriores. Vamos ver as mudanças requeridas, estas serão suficientes, para qualquer modelo que use 2 indicadores ao mesmo tempo.

Vamos começar vendo as declarações das variáveis:

class C_Automaton : public C_Manager
{
        protected:
                enum eTrigger {TRIGGER_NONE, TRIGGER_BUY, TRIGGER_SELL};
        private :
                enum eSelectMedia {MEDIA_FAST, MEDIA_SLOW};
                struct st00
                {
                        int     Shift,
                                nBars;
                        double  OverBought,
                                OverSold;
                }m_Infos;
                struct st01
                {
                        double  Buff[];
                        int     Handle;
                }m_Op[sizeof(eSelectMedia) + 1];
                int     m_nBars;
                ENUM_TIMEFRAMES m_TF;

Aqui temos uma enumeração, que irá nos ajudar a acessar, usando uma linguagem de alto nível, os dados das médias, de forma que não iremos nos confundir durante a programação do calculo.

A próxima coisa que temos, é uma estrutura que irá nos permitir, acessar tanto o buffer, quanto o handle do indicador. Mas notem com atenção o fato de que esta estrutura esta sendo declarada como sendo um array, e o tamanho deste array é justamente a quantidade de dados presentes na enumeração mais 1. Ou seja, não importa a quantidade de coisas que iremos utilizar, tudo que precisamos fazer é adicionar elas a enumeração. Desta forma o array, irá se adequar ao modelo final a ser construído.

De certa forma, isto é muito mais adequado, do que o modelo usado como sendo padrão da classe. Mas já que podíamos utilizar um modelo mais simples, este foi implantado primeiro. Acredito assim, que o entendimento fica sendo bem mais adequado e garantido.

Muito bem, agora você já sabe como adicionar diversos indicadores, de forma bastante simples na classe C_Automaton. Mas vamos ver como de fato inicializar as coisas, no constructor da classe, isto pode ser visto logo abaixo:

                C_Automaton(const ulong magic, double FinanceStop, double FinanceTake, uint Leverage,
                                                bool IsDayTrade, double Trailing, const ENUM_TIMEFRAMES iPeriod,
                                                const double OverBought = 70, const double OverSold = 30, const int iShift = 1)
                        :C_Manager(magic, FinanceStop, FinanceTake, Leverage, IsDayTrade, Trailing, true, 10),
                         m_TF(iPeriod)
                        {
                                for (int c0 = sizeof(eSelectMedia); c0 <= 0; c0--)
                                {
                                        m_Op[c0].Handle = INVALID_HANDLE;
                                        ArraySetAsSeries(m_Op[c0].Buff, true);
                                }
                                m_Infos.Shift      = (iShift < 3 ? 3 : iShift);
                                m_Infos.OverBought = OverBought;
                                m_Infos.OverSold   = OverSold;
                                m_nBars = iBars(NULL, m_TF);
                                m_Op[MEDIA_FAST].Handle = iMA(NULL, m_TF, 9, 0, MODE_EMA, PRICE_CLOSE);
                                m_Op[MEDIA_SLOW].Handle = iMA(NULL, m_TF, 20, 0, MODE_SMA, PRICE_CLOSE);
                        }

Aqui a mágica começa a acontecer, observem como faço para inicializar, por padrão todos os indicadores, independentemente do numero deles. A maneira mais simples, é por meio deste laço. Você não precisa de fato, se preocupar se terá mais ou menos indicadores, sendo informados na enumeração. Isto não importa de fato, pois este laço, irá conseguir cobrir todos eles.

Agora vem a real preocupação que temos que de fato ter. Aqui todos os indicadores irão usar o mesmo tempo gráfico, mas pode acontecer de que você use tempos gráficos diferentes, para cada um dos indicadores. Neste caso, será preciso ajustar um pouco o código deste constructor. Mas as mudanças serão bem poucas, e pertinentes a apenas aquele modelo especifico, que você estará criando.

Mas preste atenção ao momento de informar isto, quando for capturar os handles a serem utilizados depois. Você os deve capturar, de maneira que cada um dos indicadores, seja corretamente instanciado. Se isto não for feito de uma maneira correta, você poderá, com certeza, ter problemas no seu modelo. Aqui no caso, estou indicando que irei utilizar a média móvel exponencial de 9 períodos, e a média móvel aritmética de 20 períodos no sistema. Mas nada impede de você fazer combinações entre indicadores diferentes, e distintos desde é claro, isto venha de alguma forma a servir ao seu sistema operacional.

Nota importante: Vale mencionar uma coisa especifica aqui: Caso você esteja utilizando um indicador personalizado, do qual você criou. Não será de fato necessário, que ele esteja no gráfico do ativo. Mas como você não o encontrará entre os indicadores padrão, para iniciar o handle, a fim de obter os dados deste indicador personalizado, você deverá usar a função iCustom. veja na documentação, como utilizar esta função a fim de conseguir acessar o seu indicador personalizado. Novamente, ele não necessariamente precisará estar no gráfico.

Quero que reparem neste ponto especifico. É muito importante que você se atende a ele. Quando no EA, não informamos um valor de deslocamento, não poderemos de fato usar o valor padrão. Isto por conta que se for utilizado o valor padrão, teremos problemas em de fato testar o cruzamento das médias, temos que indicar um valor mínimo de deslocamento, e este é o valor 3, até poderiamos utilizar o valor 2, caso você queira um gatilho mais sensível. No entanto não podemos utilizar o valor de 1, pois ele não nos permite, fazer uma analise adequada. Para entender o motivo é preciso ver como o calculo funciona.

Uma vez que o constructor, inicialize corretamente os dados que iremos utilizar,  precisaremos confeccionar a parte responsável pelos cálculos, a fim de que o mecanismo de disparo, consiga fazer com que o EA opere automaticamente. E no caso de um mecanismo com múltiplos indicadores, precisamos que o sistema faça a coisa de uma forma um pouco diferente, da que era feita quando usarmos apenas um único indicador. Isto pode ser notado ao ver o código abaixo:

inline eTrigger CheckTrigger(void)
                        {
                                int iRet;
                                bool bOk = false;
                                        
                                if (iRet = iBars(NULL, m_TF)) > m_nBars)
                                {
                                        for (int c0 = sizeof(eSelectMedia); c0 <= 0; c0--)
                                        {
                                                if (m_Op[c0].Handle == INVALID_HANDLE) return TRIGGER_NONE;
                                                if (CopyBuffer(m_Op[c0].Handle, 0, 0, m_Infos.Shift + 1, m_Op[c0].Buff) < m_Infos.Shift + 1) return TRIGGER_NONE;
                                                bOk = true;
                                        }
                                        if (!bOk) return TRIGGER_NONE; else m_nBars = iRet;
                                        if ((m_Op[MEDIA_FAST].Buff[1] > m_Op[MEDIA_SLOW].Buff[1]) && (m_Op[MEDIA_FAST].Buff[m_Infos.Shift] < m_Op[MEDIA_SLOW].Buff[m_Infos.Shift])) return TRIGGER_BUY;
                                        if ((m_Op[MEDIA_FAST].Buff[1] < m_Op[MEDIA_SLOW].Buff[1]) && (m_Op[MEDIA_FAST].Buff[m_Infos.Shift] > m_Op[MEDIA_SLOW].Buff[m_Infos.Shift])) return TRIGGER_SELL;
                                };
                                return TRIGGER_NONE;
                        }

Aqui temos um laço, que irá fazer algumas coisa de forma que não iremos de fato precisar nos preocupar em quantos indicadores, estaremos utilizando. Se bem que todos eles, deverão ser corretamente inicializados no constructor, caso contrário a fase de calculo, que será executada aqui, não irá de fato conseguir gerar nenhum tipo de gatilho, seja ele para compra ou venda.

A primeira coisa que faremos, é verificar se o handle do indicador, foi de fato inicializado. Caso isto não tenha sido feito, não temos de fato um gatilho. Feito isto, podemos capturar os dados do buffer do indicador, se você estiver na duvida sobre como usar esta função de captura dos dados do indicador, veja a função CopyBuffer na documentação. Isto será de grande ajuda, caso você esteja utilizando um indicador personalizado, que você mesmo construiu.

Uma vez que temos todos os indicadores, com seus respectivos buffers capturados, podemos passar para a parte do calculo em si. Esta é feita a partir de agora. Mas espera um pouco .... que código maluco é este antes do calculo ?!?! Este código serve para evitar, que caso tenhamos colocado uma lista nula de enumeradores, o sistema de calculo, não seja de modo algum acionado. Sem este código, o calculo poderia ser acionado, caso tivessemos uma lista nula de enumeradores. O que quebraria completamente a robustez do sistema, que estaria sendo projetado. Mas vamos voltar ao sistema de cálculos, e agora, por estarmos usando cruzamento de médias, temos que tomar um cuidado muito especial aqui.

Observem o fato de não estarmos, de maneira imprudente, testando o valor zero ( o mais recente ) presente no buffer, e por que disto ?!?! O motivo é que antes do tempo gráfico de fato ser encerrado, podemos ter um falso cruzamento das médias, e se isto acontecer, teríamos um disparo acidental.

Mas o sistema, não irá testar os buffers, somente quando uma nova barra fosse gerada ?!?! Sim, mas se no exato momento, que a barra fosse gerada, ocorresse um mínimo, e é mínimo mesmo, cruzamento, o sistema irá gerar um disparo de ordem. Por conta disto, ignoramos o valor mais recente, e assumimos o seu anterior. Este é o motivo, de precisamos que o deslocamento, seja configurado com no mínimo 2, para sensibilidade máxima, ou 3 como esta sendo feito neste exemplo, onde o cruzamento pode acontecer um pouco mais distante da barra que está sendo plotada. Mas você pode experimentar, tentar usar um outro método de calculo, este daqui, é apenas para estudos e demonstração, de maneira alguma, deve ser usado em uma conta real.

Para finalizar este ultimo modelo, vamos ver uma outra coisa sobre o sistema. Este pode ser visto logo abaixo:

inline virtual void Triggers(void) final
                        {
#define def_HILO 20
                                if (!CtrlTimeIsPassed()) ClosePosition(); else switch (CheckTrigger())
                                {
                                        case TRIGGER_BUY:
                                                if (m_Memory == TRIGGER_SELL) ClosePosition();
                                                if (m_Memory != TRIGGER_BUY) ToMarket(ORDER_TYPE_BUY);
                                                m_Memory = TRIGGER_BUY;
                                                break;
                                        case TRIGGER_SELL:
                                                if (m_Memory == TRIGGER_BUY) ClosePosition();
                                                if (m_Memory != TRIGGER_SELL) ToMarket(ORDER_TYPE_SELL);
                                                m_Memory = TRIGGER_SELL;
                                                break;
                                }
                                LockStopInPrice(IsBuyPosition() ?  iLow(NULL, m_TF, iLowest(NULL, m_TF, MODE_LOW, def_HILO, 0)) : iHigh(NULL, m_TF, iHighest(NULL, m_TF, MODE_HIGH, def_HILO, 0)));
#undef def_HILO
                        };

A grande verdete nesta função, é justamente este código, onde teremos um trailing stop, bastante curioso. Desta forma a ordem, ou preço de stop, dependendo do caso, estará ou na máxima ou na mínima, dependendo é claro, se estaremos comprados ou vendidos, o valor a ser utilizado, é muito parecido com um indicador conhecido, por muito que operam na B3, chamado HILO. Este indicador, para quem não sabe, procura a máxima ou mínima do preço, dentro de uma determinada quantidade de barras. O código responsável por isto é justamente estes: Aqui procuramos o valor LO, e aqui o valor HI, em ambos os casos o HILO é de 20.

Com isto encerro este terceiro exemplo.


Considerações Final

Aqui, nesta pequena sequencia, mostrei como você pode desenvolver um EA, que funcione de forma automática. Tentei demonstrar de uma maneira, o mais lúdica e simples, como fazer isto. É verdade que mesmo tentando fazer isto, de uma forma simples, ainda assim, será preciso que você venha a estudar, e dedicar um pouco de tempo, a fim de aprender de fato com fazer a coisa toda.

Mostrei as principais falhas, problemas e dificuldades, envolvidas no que rege o trabalho de um programador, a fim de conseguir de fato criar um EA, que funcione automaticamente. Mas também mostrei, que é algo que lhe trará muito conhecimento, e uma mudança na maneira de observar de fato o mercado.

Tentei deixar as coisas, de forma que você consiga de fato criar um sistema, que seja seguro, confiável e robusto. Ao mesmo tempo, que ele seja modular, compacto e bastante leve. Podendo ser usado em conjunto com diversas outras coisas. Pois nada adianta, você ter um sistema, que não lhe permita operar uma variedade de coisas ao mesmo tempo. Pois nem sempre você conseguirá de fato fazer um bom ganho, operando apenas e somente um único ativo.

Talvez a grande parte de fato, venha a se interessar por este último artigo da sequencia, onde explico em 3 exemplos práticos. Se bem que o conhecimento, de toda a sequencia de artigos, é necessário para que você consiga de fato tirar proveito deste artigo daqui, em especifico. Mas de uma maneira bem simples, acredito que tenho conseguido passar a ideia, de que não é preciso de fato ser um gênio ou prodígio em programação, ou ter vários cursos e graduações. É preciso apenas que você de fato entenda como a plataforma MetaTrader 5 funciona, como a linguagem MQL5 trabalha, para assim conseguir fazer o que deseja, seja lá o que for.

Apresentei também, como você pode criar circunstâncias especificas, para que o seu sistema funcione e trabalhe, mesmo quando aparentemente o MQL5 ou o MetaTrader 5, não tenham em seu portfólio, o indicador que você desejaria usar. Isto foi feito no exemplo 3, onde mostrei como você faz para criar internamente, o indicador HILO, assim como foi feito ali, podemos também criar outros, desenvolver um especifico para ser utilizado no EA automatizado. Mas independentemente disto, precisamos que o sistema, sempre seja corretamente implementado e testado, pois nada adianta você criar um sistema maravilhoso, que no final não lhe dará nenhum lucro, apenas prejuízo.

Como último recado nesta sequencia, aqui não expliquei todas as coisas possíveis de serem colocadas, mesmo por que, a ideia não é de fato minuciar todos os detalhes a ponto de criar uma biblioteca de modelagem, de maneira a sempre se criar um EA automático. Se bem que a ideia parece bastante válida, mas isto tomaria muito tempo, já que para de fato um modelo se tornar uma biblioteca, ele precisa de tempo para ser totalmente testado. Mas voltaremos neste ponto em uma nova serie de artigos, que em breve estará sendo publicada, onde explicarei como fazer para desenvolver uma ferramenta bastante curiosa, e extremamente útil, para aqueles que estão entrando no mercado.

Vale lembrar que o verdadeiro teste de qualquer EA automático, não irá realmente se dá no testador de estratégia, presente na plataforma MetaTrader 5. O verdadeiro teste, será feito em uma conta DEMO, com o mercado em pleno funcionamento. Já que ali, não haverá nenhum tipo de ajuste, a fim de que o EA, possa parecer ser de fato melhor do que realmente é ... Desta forma me despeço finalizando esta pequena sequencia, e nos veremos na próxima sequencia que esta por vim ... No anexo, você terá acesso a todos os código visto nesta sequencia, isto para serem estudados e analisados, a fim de que você compreenda de fato como o EA automático funciona.


NOTA IMPORTANTE: Não utilize os EA disponíveis no anexo, sem o devido conhecimento. Tais EA, são apenas para uso demonstrativo e educacional.

Se for fazer uso deles em uma conta REAL, faça assumindo o risco, já que eles podem gerar perdas substanciais ao seu patrimônio.


Arquivos anexados |
Últimos Comentários | Ir para discussão (6)
Daniel Jose
Daniel Jose | 30 mar 2023 em 17:42
César Augusto #:

Olá Daniel, primeiramente gostaria de parabeniza-lo pelo desempenho em colaborar com a comunidade de desenvolvedores e entusiastas.

Estou com uma dúvida na execução do EA desta sequencia de artigos.

Ele não está dando entradas, testei em conta Netting e Hedging trocando de Ordem OCO para Pendente, conforme sua documentação. Mas as ordens não são executadas.

E também não dá nenhum erro no log.

O que poderia ser?

Obs: não alterei nada no código, somente a logica de compra e venda... testei com minha logica (minha estratégia) não deu entradas, pensei: fiz alguma merda, kkkk.... ai peguei seu codigo sem nenhuma alteração, compilei e acontece a mesma coisa.

Executa no manual (quando acerta a quantidade de lote), mas automático não.

As entradas automáticas somente acontecem quando os parâmetros de entrada, que você definiu permitem o disparo da ordem. Ou seja se a sua lógica dizer que tal indicador, seja ele qual for, precisa ter um dado valor, a entrada ou saída automática somente acontecerá quando o valor for atingido. Caso contrário, ela jamais irá acontecer. Você deve ter visto que existem 3 exemplos de setups na sequencia. Experimente e estude cada um deles, para entender como a lógica de disparo acontece. Sem entender esta lógica de disparo, você não conseguirá fazer o mecanismo automático funcionar. Ele é como se fosse uma ratoeira, se você armar ela para pegar um elefante, ela não será disparada quando o rato mexer nela 😂 ... De qualquer forma estude os mecanismo que estão nos exemplos e boa sorte ... 😁👍

FernandoN23
FernandoN23 | 6 jun 2023 em 03:01

Daniel, obrigado pela sua dedicação em ensinar.

Você pode, por favor, verificar o funcionamento do EA desta parte 15 - versão v3 - que usa a C_Automaton_v3.mqh?

A definição da lista eSelectMedia é clara. Entretanto, acho que a sua utilização tem algum problema, no que diz respeito à manipulação dos elementos dos arrays m_Op no loop FOR, especificamente com "sizeof(eSelectMedia)".

Segundo a documentação (https://www.mql5.com/en/docs/basis/types/integer/enumeration), o sizeof sempre retorna 4, mesmo para enumerações com mais elementos.

Daniel Jose
Daniel Jose | 6 jun 2023 em 16:00
FernandoN23 #:

Daniel, obrigado pela sua dedicação em ensinar.

Você pode, por favor, verificar o funcionamento do EA desta parte 15 - versão v3 - que usa a C_Automaton_v3.mqh?

A definição da lista eSelectMedia é clara. Entretanto, acho que a sua utilização tem algum problema, no que diz respeito à manipulação dos elementos dos arrays m_Op no loop FOR, especificamente com "sizeof(eSelectMedia)".

Segundo a documentação (https://www.mql5.com/en/docs/basis/types/integer/enumeration), o sizeof sempre retorna 4, mesmo para enumerações com mais elementos.

Você está confundindo as coisas. Este 4 que a documentação diz é o tamanho, em termos de bytes, usado no retorno de sizeof e não a quantidade de elementos máximos que será retornado.

FernandoN23
FernandoN23 | 7 jun 2023 em 14:33
Daniel Jose #:

Você está confundindo as coisas. Este 4 que a documentação diz é o tamanho, em termos de bytes, usado no retorno de sizeof e não a quantidade de elementos máximos que será retornado.

Daniel, obrigado pela resposta rápida.

Ainda sobre o FOR com o sizeof(enum), adiciono um script de teste, o resultado obtido e mais uma pergunta, logo abaixo.

Agradeço a orientação.

script teste.mq5

//+------------------------------------------------------------------+
void OnStart()
  {
      enum eSelectMedia {MEDIA_FAST, MEDIA_SLOW};
      enum eSelectMeses {JANEIRO, FEVEREIRO, MARCO, ABRIL, MAIO, JUNHO, JULHO, AGOSTO, SETEMBRO, OUTUBRO, NOVEMBRO, DEZEMBRO};
   
      struct st01
         {
               double  Buff[];
               int     Handle;
         }m_Op[sizeof(eSelectMedia) + 1];
                   
      Print("Tamanho do eSelectMedia = ", sizeof(eSelectMedia));
      Print("Tamanho do eSelectMeses = ", sizeof(eSelectMeses));
      Print("Tamanho do m_Op = ", ArraySize(m_Op));
      
      Print("========= Mostrar enum eSelectMedia =========");
      for (int i = sizeof(eSelectMedia); i >= 0; i--)
        {
            Print("eSelectMedia - idx = ", i);
        }
   
      Print("========= Mostrar enum eSelectMeses =========");
      for (int i = sizeof(eSelectMeses); i >= 0; i--)
        {
            Print("eSelectMeses - idx = ", i);
        }
   
      Print("========= Mostrar enum m_Op =========");
      for (int i = ArraySize(m_Op); i >= 0; i--)
        {
            Print("m_Op - idx = ", i);
        }     
  }
// Fim OnStart()  
//+------------------------------------------------------------------+


Resultado:

2023.06.07 09:09:10.415    teste (EURUSD,M1)    Tamanho do eSelectMedia = 4
2023.06.07 09:09:10.415    teste (EURUSD,M1)    Tamanho do eSelectMeses = 4
2023.06.07 09:09:10.415    teste (EURUSD,M1)    Tamanho do m_Op = 5
2023.06.07 09:09:10.415    teste (EURUSD,M1)    ========= Mostrar enum eSelectMedia =========
2023.06.07 09:09:10.415    teste (EURUSD,M1)    eSelectMedia - idx = 4
2023.06.07 09:09:10.415    teste (EURUSD,M1)    eSelectMedia - idx = 3
2023.06.07 09:09:10.415    teste (EURUSD,M1)    eSelectMedia - idx = 2
2023.06.07 09:09:10.415    teste (EURUSD,M1)    eSelectMedia - idx = 1
2023.06.07 09:09:10.415    teste (EURUSD,M1)    eSelectMedia - idx = 0
2023.06.07 09:09:10.415    teste (EURUSD,M1)    ========= Mostrar enum eSelectMeses =========
2023.06.07 09:09:10.415    teste (EURUSD,M1)    eSelectMeses - idx = 4
2023.06.07 09:09:10.415    teste (EURUSD,M1)    eSelectMeses - idx = 3
2023.06.07 09:09:10.415    teste (EURUSD,M1)    eSelectMeses - idx = 2
2023.06.07 09:09:10.415    teste (EURUSD,M1)    eSelectMeses - idx = 1
2023.06.07 09:09:10.415    teste (EURUSD,M1)    eSelectMeses - idx = 0
2023.06.07 09:09:10.415    teste (EURUSD,M1)    ========= Mostrar enum m_Op =========
2023.06.07 09:09:10.415    teste (EURUSD,M1)    m_Op - idx = 5
2023.06.07 09:09:10.415    teste (EURUSD,M1)    m_Op - idx = 4
2023.06.07 09:09:10.415    teste (EURUSD,M1)    m_Op - idx = 3
2023.06.07 09:09:10.415    teste (EURUSD,M1)    m_Op - idx = 2
2023.06.07 09:09:10.415    teste (EURUSD,M1)    m_Op - idx = 1
2023.06.07 09:09:10.415    teste (EURUSD,M1)    m_Op - idx = 0


O enum eSelectMedia tem 2 elementos.
O enum eSelectMeses tem 12 elementos.
O retorno de sizeof() é 4, para qualquer um deles, conforme a documentação.

O m_Op tem ArraySize() = 5, porque foi definido com base no sizeof(eSelectMedia) + 1, conforme o arquivo C_Automaton_v3.mqh.

Quando usei o sizeof(enum) no loop FOR, não foi considerada a quantidade de elementos no enum correspondente. A interação considerou 4, que é o retorno de sizeof(enum), tanto para o enum com 2 elementos quanto para o enum com 12 elementos.

Com isto, como devo criar um loop que considere a quantidade exata de elementos de uma enumeração?

Código formatado incorrectamente editado pelo moderador.

Fernando Carreiro
Fernando Carreiro | 7 jun 2023 em 14:52
@FernandoN23 #: script teste.mq5

Por favor utilize o botão do CÓDIGO (Alt -S) ao inserir o seu código.

Botão de código no editor

Algoritmos de otimização populacionais: Otimizador lobo-cinzento (GWO) Algoritmos de otimização populacionais: Otimizador lobo-cinzento (GWO)
Vamos falar sobre um dos algoritmos de otimização mais recentes e modernos: o "Packs of grey wolves" (manada de lobos-cinzentos). Devido ao seu comportamento distinto em funções de teste, este algoritmo se torna um dos mais interessantes em comparação com outros considerados anteriormente. Ele é um dos principais candidatos para treinamento de redes neurais e para otimizar funções suaves com muitas variáveis.
Algoritmos de otimização populacionais: Colônia artificial de abelhas (Artificial Bee Colony, ABC) Algoritmos de otimização populacionais: Colônia artificial de abelhas (Artificial Bee Colony, ABC)
Hoje estudaremos o algoritmo de colônia artificial de abelhas. Complementaremos nosso conhecimento com novos princípios para estudar espaços funcionais. E neste artigo falarei sobre minha interpretação da versão clássica do algoritmo.
Algoritmos de otimização populacionais: Algoritmo de otimização de cuco (COA) Algoritmos de otimização populacionais: Algoritmo de otimização de cuco (COA)
O próximo algoritmo que abordaremos será a otimização de busca de cuco usando voos Levy. Este é um dos algoritmos de otimização mais recentes e um novo líder na tabela de classificação.
DoEasy. Controles (Parte 26): Apurando o objeto WinForms "ToolTip" e desenvolvendo o "ProgressBar". DoEasy. Controles (Parte 26): Apurando o objeto WinForms "ToolTip" e desenvolvendo o "ProgressBar".
Neste artigo vamos completar o desenvolvimento do controle ToolTip e começar a desenvolver o objeto WinForms ProgressBar. Ao trabalharmos nesses objetos, desenvolveremos uma funcionalidade versátil para animar os controles e seus componentes.