English Русский Español Deutsch 日本語
preview
Paradigmas de programação (Parte 1): Abordagem procedural para desenvolvimento de Expert Advisors com base na dinâmica de preços

Paradigmas de programação (Parte 1): Abordagem procedural para desenvolvimento de Expert Advisors com base na dinâmica de preços

MetaTrader 5Exemplos | 22 maio 2024, 11:06
98 0
Kelvin Muturi Muigua
Kelvin Muturi Muigua

Introdução

No mundo do desenvolvimento de software, os paradigmas de programação são princípios orientadores para escrever e organizar código. Assim como a escolha de diferentes rotas para alcançar um destino, existem diferentes abordagens ou paradigmas de programação para realizar tarefas usando MQL5.

Em dois artigos, abordaremos as principais paradigmas de programação necessárias para criar ferramentas de negociação com MQL5. Meu objetivo é compartilhar métodos eficientes e avançados que proporcionam ótimos resultados por meio de código curto e eficaz. Vou explicar cada estilo de programação e demonstrar isso criando um Expert Advisor totalmente funcional.

Tipos de paradigmas de programação

Existem três principais paradigmas de programação que todo desenvolvedor de MQL5 deve conhecer:

  1. Programação procedural: Este artigo é dedicado a este paradigma.
  2. Programação funcional: Este paradigma também será abordado no artigo, pois é muito semelhante à programação procedural.
  3. Programação orientada a objetos (POO): Este paradigma será discutido no próximo artigo.
Cada paradigma possui suas próprias regras e propriedades únicas, destinadas a resolver tarefas específicas e a desenvolver ferramentas de negociação de forma eficiente usando MQL5.


Programação procedural

A programação procedural é uma abordagem sistemática e passo a passo para escrever código. Ela envolve dividir qualquer problema em uma sequência de instruções precisas, muito parecido com cozinhar um prato seguindo uma receita. Os programadores criam um caminho claro para o computador, fornecendo-lhe um algoritmo passo a passo para alcançar o resultado desejado.

Independentemente de você ser um novato em programação ou estar apenas interessado na organização do código, a programação procedural fornece um ponto de entrada simples e intuitivo para o tema.

Principais propriedades da programação procedural


  1. Funções:
    No centro da programação procedural estão as funções. Elas são conjuntos de instruções agrupadas para realizar uma tarefa específica. As funções encapsulam funcionalidades, garantindo modularidade e reutilização do código.
  2. Design de cima para baixo:
    Na programação procedural, frequentemente utiliza-se uma abordagem de design de cima para baixo. Os desenvolvedores dividem o problema em subtarefas menores e mais gerenciáveis. Cada subtarefa é resolvida individualmente, contribuindo para a solução geral.
  3. Estilo imperativo:
    O caráter imperativo, ou de comando, da programação procedural implica declarações explícitas que alteram o estado do programa. Os desenvolvedores indicam como o programa deve resolver uma tarefa por meio de uma série de comandos procedurais.
  4. Variáveis e dados:
    Procedimentos ou funções na programação procedural manipulam variáveis e dados. Essas variáveis podem armazenar valores que mudam durante a execução do programa. As mudanças de estado são um aspecto fundamental da programação procedural.
  5. Execução sequencial:
    A execução do programa ocorre de forma sequencial. Os comandos são executados um após o outro, e as estruturas de controle, como laços e expressões condicionais, direcionam o fluxo do programa.
  6. Modularidade:
    A programação procedural promove a modularidade, organizando o código em procedimentos ou funções. Cada módulo lida com um aspecto específico da funcionalidade do programa, melhorando a organização do código e a facilidade de manutenção.
  7. Reutilização:
    A capacidade de reutilizar código é uma vantagem chave da programação procedural. Depois que uma função é escrita e testada, ela pode ser usada em qualquer lugar onde essa funcionalidade específica seja necessária no programa, reduzindo a redundância e aumentando a eficiência.
  8. Legibilidade:
    O código procedural geralmente é mais legível, especialmente para aqueles acostumados à abordagem passo a passo. O fluxo linear de execução permite seguir facilmente a lógica do programa.

Programação funcional

A programação funcional gira em torno do conceito de funções como cidadãos de primeira classe e imutabilidade. O paradigma é semelhante à programação procedural, exceto pelo princípio fundamental de como os dados são manipulados e as tarefas são executadas.

Ao contrário da programação procedural, onde os dados podem mudar durante a execução do programa, a programação funcional prefere um ambiente mais estável. Depois de criados, os dados permanecem imutáveis. A adesão à imutabilidade garante um certo nível de previsibilidade e ajuda a prevenir efeitos colaterais inesperados no código.

Principais propriedades da programação funcional



  1. Imutabilidade:
    Princípio central da programação funcional. Depois de criados, os dados permanecem imutáveis. Em vez de modificar os dados existentes, são criados novos dados com as alterações necessárias. Isso proporciona previsibilidade e ajuda a evitar efeitos colaterais inesperados.
  2. Funções como cidadãos de primeira classe:
    As funções são tratadas como elementos de primeira classe, o que significa que podem ser atribuídas a variáveis, passadas como argumentos para outras funções e retornadas como resultados de outras funções. Essa flexibilidade permite a criação de funções de ordem superior e promove um estilo de codificação mais modular e expressivo.
  3. Estilo declarativo:
    A programação funcional prefere um estilo declarativo, onde o foco está no que o programa deve alcançar, em vez de como alcançá-lo. Isso promove a criação de um código mais conciso e legível.
  4. Evitar estado mutável:
    O estado mutável é minimizado ou eliminado. Os dados são considerados imutáveis e as funções evitam alterar o estado externo. Essa característica simplifica o raciocínio sobre o comportamento das funções.
  5. Recursão e funções de ordem superior:
    Na programação funcional, recursão e funções de ordem superior são frequentemente usadas, onde funções recebem outras funções como argumentos ou as retornam como resultados. Isso leva à criação de um código mais modular e reutilizável.


Abordagem procedural para o desenvolvimento de um Expert Advisor baseado na dinâmica de preço (Price Action)

Agora que aprofundamos nas essências dos paradigmas de programação procedural e funcional, vamos aplicar a teoria na prática, vamos criar um Expert Advisor baseado na dinâmica de preço. Primeiro, vou falar sobre a estratégia de negociação que vamos automatizar. Depois, vamos passar pelos vários componentes do código, explicando sua funcionalidade e como trabalham juntos de forma coesa.


Estratégia de dinâmica de preço com o indicador EMA


Nossa estratégia de negociação é baseada em um indicador conhecido como média móvel exponencial (Exponential Moving Average, EMA). O indicador é amplamente utilizado na análise técnica e ajuda a determinar a direção do mercado com base na configuração de negociação escolhida. A média móvel é um indicador padrão no MQL5, o que facilita sua inclusão no nosso código.


Compra:
Abrimos uma posição de compra quando a última vela fechada é de alta e seus preços mínimo e máximo estão acima da média móvel exponencial (EMA).

Sinal de compra na estratégia de dinâmica de preço com EMA


Venda:
Abrimos uma posição de venda quando a última vela fechada é de baixa e seus preços mínimo e máximo estão abaixo da média móvel exponencial (EMA).

Sinal de venda na estratégia de dinâmica de preço com EMA


Saída:
Fechamos automaticamente todas as posições abertas e obtemos o lucro ou prejuízo correspondente quando se atinge a porcentagem de lucro ou prejuízo definida pelo usuário para a conta, ou usamos ordens tradicionais de stop-loss ou take-profit.


Configuração Condições
Compra A última vela fechada é de alta e seus preços mínimo e máximo estão acima da média móvel exponencial (EMA).
Venda A última vela fechada é de baixa e seus preços mínimo e máximo estão abaixo da média móvel exponencial (EMA).
Saída  Fechamos todas as posições abertas e obtemos o lucro ou prejuízo correspondente quando se atinge a porcentagem de lucro/prejuízo definida pelo usuário para a conta ou é ativado o stop-loss/take-profit.


Implementação da estratégia de negociação no código

Agora que estabelecemos nossas regras e plano de negociação, vamos implementar nossa estratégia de negociação, escrevendo o código em MQL5 no ambiente MetaEditor. Siga os passos a seguir para começar com um template limpo de Expert Advisor, contendo apenas as funções obrigatórias necessárias:


Passo 1: Abra o MetaEditor e inicie o Assistente clicando no botão "Criar".

Criando um novo Expert Advisor no Assistente MQL5


Passo 2: Selecione "Expert Advisor (template)" e clique em "Avançar".

Criando um novo Expert Advisor no Assistente MQL5


Passo 3: Em "Parâmetros gerais", insira o nome do Expert Advisor e clique em "Avançar".

Criando um novo Expert Advisor no Assistente MQL5


Passo 4: Certifique-se de que nenhuma opção está selecionada na seção "Manipuladores de eventos". Desmarque as caixas de seleção, se houver, e clique em "Avançar".

Criando um novo Expert Advisor no Assistente MQL5


Passo 5: Certifique-se de que nenhuma opção está selecionada na seção "Manipuladores de eventos de teste". Desmarque todas as opções, se houver, e clique em "Concluir" para criar o template do Expert Advisor em MQL5.

Criando um novo Expert Advisor no Assistente MQL5


Agora temos um template limpo de Expert Advisor em MQL5 com as funções obrigatórias (OnInit, OnDeinit e OnTick). Não se esqueça de salvar o novo arquivo antes de continuar.

Veja como está o código recém-gerado do nosso Expert Advisor:

//+------------------------------------------------------------------+
//|                                               PriceActionEMA.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+


Para começar, vamos escrever uma breve descrição do Expert Advisor, declarar e inicializar variáveis personalizadas, e depois declarar todas as variáveis globais. Coloque este código logo antes da função OnInit().

#property description "A price action EA to demonstrate how to "
#property description "implement the procedural programming paradigm."

//--User input variables
input long magicNumber = 101;//Magic Number (Set 0 [Zero] to disable

input group ""
input ENUM_TIMEFRAMES tradingTimeframe = PERIOD_H1;//Trading Timeframe
input int emaPeriod = 20;//Moving Average Period
input int emaShift = 0;//Moving Average Shift

input group ""
input bool enableTrading = true;//Enable Trading
input bool enableAlerts = false;//Enable Alerts

input group ""
input double accountPercentageProfitTarget = 10.0;//Account Percentage (%) Profit Target
input double accountPercentageLossTarget = 10.0;//Account Percentage (%) Loss Target

input group ""
input int maxPositions = 3;//Max Positions (Max open positions in one direction)
input int tp = 5000;//TP (Take Profit Points/Pips [Zero (0) to diasable])
input int sl = 10000;//SL (Stop Loss Points/Pips [Zero (0) to diasable])


Como já sabemos, "a base da programação procedural são as funções. Elas são conjuntos de instruções agrupadas para realizar uma tarefa específica. As funções encapsulam a funcionalidade, garantindo modularidade e reutilização de código". Vamos criar funções personalizadas.

Função GetInit:

Essa função é responsável por inicializar todas as variáveis globais e executar outras tarefas ao carregar ou inicializar o Expert Advisor.

int GetInit() //Function to initialize the robot and all the variables
  {
   int returnVal = 1;
//create the iMA indicator
   emaHandle = iMA(Symbol(), tradingTimeframe, emaPeriod, emaShift, MODE_EMA, PRICE_CLOSE);
   if(emaHandle < 0)
     {
      Print("Error creating emaHandle = ", INVALID_HANDLE);
      Print("Handle creation: Runtime error = ", GetLastError());
      //force program termination if the handle is not properly set
      return(-1);
     }
   ArraySetAsSeries(movingAverage, true);

//reset the count for positions
   totalOpenBuyPositions = 0;
   totalOpenSellPositions = 0;
   buyPositionsProfit = 0.0;
   sellPositionsProfit = 0.0;
   buyPositionsVol = 0.0;
   sellPositionsVol = 0.0;

   closedCandleTime = iTime(_Symbol, tradingTimeframe, 1);

   startingCapital = AccountInfoDouble(ACCOUNT_EQUITY);//used to calculate the account percentage profit

   if(enableAlerts)
     {
      Alert(MQLInfoString(MQL_PROGRAM_NAME), " has just been LOADED in the ", Symbol(), " ", EnumToString(Period()), " period chart.");
     }

//structure our comment string
   commentString = "\n\n" +
                   "Account No: " + IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN)) +
                   "\nAccount Type: " + EnumToString((ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE)) +
                   "\nAccount Leverage: " + IntegerToString(AccountInfoInteger(ACCOUNT_LEVERAGE)) +
                   "\n-----------------------------------------------------------------------------------------------------";
   return(returnVal);
  }


Função GetDeinit:

Essa função é responsável por liberar toda a memória usada, limpar os comentários no gráfico e executar outras tarefas de desinicialização antes de remover o Expert Advisor.

void GetDeinit()  //De-initialize the robot on shutdown and clean everything up
  {
   IndicatorRelease(emaHandle); //delete the moving average handle and deallocate the memory spaces occupied
   ArrayFree(movingAverage); //free the dynamic arrays containing the moving average buffer data

   if(enableAlerts)
     {
      Alert(MQLInfoString(MQL_PROGRAM_NAME), " has just been REMOVED from the ", Symbol(), " ", EnumToString(Period()), " period chart.");
     }
//delete and clear all chart displayed messages
   Comment("");
  }

Função GetEma:

Essa função extrai o valor da média móvel exponencial e determina a direção do mercado, comparando o valor da EMA com os preços de abertura, fechamento, máxima e mínima da vela fechada recentemente. Ela armazena o valor da direção do mercado em variáveis globais para processamento posterior por outras funções.

void GetEma()
  {
//Get moving average direction
   if(CopyBuffer(emaHandle, 0, 0, 100, movingAverage) <= 0)
     {
      return;
     }
   movingAverageTrend = "FLAT";
   buyOk = false;
   sellOk = false;
   if(movingAverage[1] > iHigh(_Symbol, tradingTimeframe, 1) && movingAverage[1] > iLow(_Symbol, tradingTimeframe, 1))
     {
      movingAverageTrend = "SELL/SHORT";
      if(iClose(_Symbol, tradingTimeframe, 1) < iOpen(_Symbol, tradingTimeframe, 1))
        {
         sellOk = true;
         buyOk = false;
        }
     }
   if(movingAverage[1] < iHigh(_Symbol, tradingTimeframe, 1) && movingAverage[1] < iLow(_Symbol, tradingTimeframe, 1))
     {
      movingAverageTrend = "BUY/LONG";
      if(iClose(_Symbol, tradingTimeframe, 1) > iOpen(_Symbol, tradingTimeframe, 1))
        {
         buyOk = true;
         sellOk = false;
        }
     }
  }


Função GetPositionsData:

Essa função examina todas as posições abertas, salvando suas propriedades, como soma de lucros, número total de posições abertas, número total de posições abertas de compra e venda, bem como o volume total/lote de cada tipo de posição. Ela exclui dados de posições que não foram abertas pelo nosso Expert Advisor ou que não correspondem ao número mágico do Expert Advisor.

void GetPositionsData()
  {
//get the total number of all open positions and their status
   if(PositionsTotal() > 0)
     {
      //variables for storing position properties values
      ulong positionTicket;
      long positionMagic, positionType;
      string positionSymbol;
      int totalPositions = PositionsTotal();

      //reset the count
      totalOpenBuyPositions = 0;
      totalOpenSellPositions = 0;
      buyPositionsProfit = 0.0;
      sellPositionsProfit = 0.0;
      buyPositionsVol = 0.0;
      sellPositionsVol = 0.0;

      //scan all the open positions
      for(int x = totalPositions - 1; x >= 0; x--)
        {
         positionTicket = PositionGetTicket(x);//gain access to other position properties by selecting the ticket
         positionMagic = PositionGetInteger(POSITION_MAGIC);
         positionSymbol = PositionGetString(POSITION_SYMBOL);
         positionType = PositionGetInteger(POSITION_TYPE);

         if(positionMagic == magicNumber && positionSymbol == _Symbol)
           {
            if(positionType == POSITION_TYPE_BUY)
              {
               ++totalOpenBuyPositions;
               buyPositionsProfit += PositionGetDouble(POSITION_PROFIT);
               buyPositionsVol += PositionGetDouble(POSITION_VOLUME);
              }
            if(positionType == POSITION_TYPE_SELL)
              {
               ++totalOpenSellPositions;
               sellPositionsProfit += PositionGetDouble(POSITION_PROFIT);
               sellPositionsVol += PositionGetDouble(POSITION_VOLUME);
              }
           }
        }
      //Get and save the account percentage profit
      accountPercentageProfit = ((buyPositionsProfit + sellPositionsProfit) * 100) / startingCapital;
     }
   else  //if no positions are open then the account percentage profit should be zero
     {
      startingCapital = AccountInfoDouble(ACCOUNT_EQUITY);
      accountPercentageProfit = 0.0;

      //reset position counters too
      totalOpenBuyPositions = 0;
      totalOpenSellPositions = 0;
     }
  }

Função TradingIsAllowed:

Essa função verifica se o Expert Advisor tem permissão para negociar pelo usuário, terminal e corretora.

bool TradingIsAllowed()
  {
//check if trading is enabled
   if(enableTrading &&
      MQLInfoInteger(MQL_TRADE_ALLOWED) && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) &&
      AccountInfoInteger(ACCOUNT_TRADE_ALLOWED) && AccountInfoInteger(ACCOUNT_TRADE_EXPERT)
     )
     {
      tradingStatus = "\n-----------------------------------------------------------------------------------------" +
                      "\nTRADING IS FULLY ENABLED! *** SCANNING FOR ENTRY ***";
      return(true);
     }
   else  //trading is disabled
     {
      tradingStatus = "\n-----------------------------------------------------------------------------------------" +
                      "\nTRADING IS NOT FULLY ENABLED! *** GIVE EA PERMISSION TO TRADE ***";
      return(false);
     }
  }

Função TradeNow:

Responsável por abrir novas posições quando todas as verificações necessárias e sinais indicam que é possível iniciar uma nova negociação.

void TradeNow()
  {
//Detect new candle formation and open a new position
   if(closedCandleTime != iTime(_Symbol, tradingTimeframe, 1))  //-- New candle found
     {
      //use the candle time as the position comment to prevent opening dublicate trades on one candle
      string positionComment = IntegerToString(iTime(_Symbol, tradingTimeframe, 1));

      //open a buy position
      if(buyOk && totalOpenBuyPositions < maxPositions)
        {
         //Use the positionComment string to check if we had already have a position open on this candle
         if(!PositionFound(_Symbol, POSITION_TYPE_BUY, positionComment)) //no position has been openend on this candle, open a buy position now
           {
            BuySellPosition(POSITION_TYPE_BUY, positionComment);
           }
        }

      //open a sell position
      if(sellOk && totalOpenSellPositions < maxPositions)
        {
         //Use the positionComment string to check if we had already have a position open on this candle
         if(!PositionFound(_Symbol, POSITION_TYPE_SELL, positionComment)) //no position has been openend on this candle, open a sell position now
           {
            BuySellPosition(POSITION_TYPE_SELL, positionComment);
           }
        }

      //reset closedCandleTime value to prevent new entry orders from opening before a new candle is formed
      closedCandleTime = iTime(_Symbol, tradingTimeframe, 1);
     }
  }

Função ManageProfitAndLoss:

Essa função verifica se os valores de lucro e perda definidos pelo usuário foram alcançados. Se as condições forem atendidas, ela liquida todo o lucro ou perda, fechando todas as posições abertas.

void ManageProfitAndLoss()
  {
//if the account percentage profit or loss target is hit, delete all positions
   double lossLevel = -accountPercentageLossTarget;
   if(
      (accountPercentageProfit >= accountPercentageProfitTarget || accountPercentageProfit <= lossLevel) ||
      ((totalOpenBuyPositions >= maxPositions || totalOpenSellPositions >= maxPositions) && accountPercentageProfit > 0)
   )
     {
      //delete all open positions
      if(PositionsTotal() > 0)
        {
         //variables for storing position properties values
         ulong positionTicket;
         long positionMagic, positionType;
         string positionSymbol;
         int totalPositions = PositionsTotal();

         //scan all the open positions
         for(int x = totalPositions - 1; x >= 0; x--)
           {
            positionTicket = PositionGetTicket(x);//gain access to other position properties by selecting the ticket
            positionMagic = PositionGetInteger(POSITION_MAGIC);
            positionSymbol = PositionGetString(POSITION_SYMBOL);
            positionType = PositionGetInteger(POSITION_TYPE);
            int positionDigits= (int)SymbolInfoInteger(positionSymbol, SYMBOL_DIGITS);
            double positionVolume = PositionGetDouble(POSITION_VOLUME);
            ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

            if(positionMagic == magicNumber && positionSymbol == _Symbol)
              {
               //print the position details
               Print("*********************************************************************");
               PrintFormat(
                  "#%I64u %s  %s  %.2f  %s [%I64d]",
                  positionTicket, positionSymbol, EnumToString(positionType), positionVolume,
                  DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN), positionDigits), positionMagic
               );

               //reset the the tradeRequest and tradeResult values by zeroing them
               ZeroMemory(tradeRequest);
               ZeroMemory(tradeResult);
               //set the operation parameters
               tradeRequest.action = TRADE_ACTION_DEAL;//type of trade operation
               tradeRequest.position = positionTicket;//ticket of the position
               tradeRequest.symbol = positionSymbol;//symbol
               tradeRequest.volume = positionVolume;//volume of the position
               tradeRequest.deviation = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);//allowed deviation from the price
               tradeRequest.magic = magicNumber;//MagicNumber of the position

               //set the price and order type depending on the position type
               if(positionType == POSITION_TYPE_BUY)
                 {
                  tradeRequest.price = SymbolInfoDouble(positionSymbol, SYMBOL_BID);
                  tradeRequest.type = ORDER_TYPE_SELL;
                 }
               else
                 {
                  tradeRequest.price = SymbolInfoDouble(positionSymbol, SYMBOL_ASK);
                  tradeRequest.type = ORDER_TYPE_BUY;
                 }

               //print the position close details
               PrintFormat("Close #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
               //send the tradeRequest
               if(OrderSend(tradeRequest, tradeResult)) //trade tradeRequest success, position has been closed
                 {
                  if(enableAlerts)
                    {
                     Alert(
                        _Symbol + " PROFIT LIQUIDATION: Just successfully closed POSITION (#" +
                        IntegerToString(positionTicket) + "). Check the EA journal for more details."
                     );
                    }
                  PrintFormat("Just successfully closed position: #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
                  PrintFormat("retcode=%u  deal=%I64u  order=%I64u", tradeResult.retcode, tradeResult.deal, tradeResult.order);
                 }
               else  //trade tradeRequest failed
                 {
                  //print the information about the operation
                  if(enableAlerts)
                    {
                     Alert(
                        _Symbol + " ERROR ** PROFIT LIQUIDATION: closing POSITION (#" +
                        IntegerToString(positionTicket) + "). Check the EA journal for more details."
                     );
                    }
                  PrintFormat("Position clossing failed: #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
                  PrintFormat("OrderSend error %d", GetLastError());//print the error code
                 }
              }
           }
        }
     }
  }

Função PrintOnChart:

Formata e exibe o status do Expert Advisor no gráfico, fornecendo ao usuário uma representação visual do status da conta e do Expert Advisor.

void PrintOnChart()
  {
//update account status strings and display them on the chart
   accountStatus = "\nAccount Balance: " + DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2) + accountCurrency +
                   "\nAccount Equity: " + DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2) + accountCurrency +
                   "\nAccount Profit: " + DoubleToString(AccountInfoDouble(ACCOUNT_PROFIT), 2) + accountCurrency +
                   "\nAccount Percentage Profit: " + DoubleToString(accountPercentageProfit, 2) + "%" +
                   "\n-----------------------------------------------------------------------------------------" +
                   "\nTotal Buy Positions Open: " + IntegerToString(totalOpenBuyPositions) +
                   "        Total Vol/Lots: " + DoubleToString(buyPositionsVol, 2) +
                   "        Profit: " + DoubleToString(buyPositionsProfit, 2) + accountCurrency +
                   "\nTotal Sell Positions Open: " + IntegerToString(totalOpenSellPositions) +
                   "        Total Vol/Lots: " + DoubleToString(sellPositionsVol, 2) +
                   "        Profit: " + DoubleToString(sellPositionsProfit, 2) + accountCurrency +
                   "\nPositionsTotal(): " + IntegerToString(PositionsTotal()) +
                   "\n-----------------------------------------------------------------------------------------" +
                   "\nJust Closed Candle:     Open: " + DoubleToString(iOpen(_Symbol, tradingTimeframe, 1), _Digits) +
                   "     Close: " + DoubleToString(iClose(_Symbol, tradingTimeframe, 1), _Digits) +
                   "     High: " + DoubleToString(iHigh(_Symbol, tradingTimeframe, 1), _Digits) +
                   "     Low: " + DoubleToString(iLow(_Symbol, tradingTimeframe, 1), _Digits) +
                   "\n-----------------------------------------------------------------------------------------" +
                   "\nMovingAverage (EMA): " + DoubleToString(movingAverage[1], _Digits) +
                   "     movingAverageTrend = " + movingAverageTrend +
                   "\nsellOk: " + IntegerToString(sellOk) +
                   "\nbuyOk: " + IntegerToString(buyOk);

//show comments on the chart
   Comment(commentString + accountStatus + tradingStatus);
  }


Função BuySellPosition:

Essa função abre novas posições de compra e venda.

bool BuySellPosition(int positionType, string positionComment)
  {
//reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);
//initialize the parameters to open a position
   tradeRequest.action = TRADE_ACTION_DEAL;
   tradeRequest.symbol = Symbol();
   tradeRequest.deviation = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
   tradeRequest.magic = magicNumber;
   tradeRequest.comment = positionComment;
   double volumeLot = NormalizeDouble(((SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN) * AccountInfoDouble(ACCOUNT_EQUITY)) / 10000), 2);

   if(positionType == POSITION_TYPE_BUY)
     {
      if(sellPositionsVol > volumeLot && AccountInfoDouble(ACCOUNT_MARGIN_LEVEL) > 200)
        {
         volumeLot = NormalizeDouble((sellPositionsVol + volumeLot), 2);
        }
      if(volumeLot < 0.01)
        {
         volumeLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
        }
      if(volumeLot > SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX))
        {
         volumeLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
        }
      tradeRequest.volume = NormalizeDouble(volumeLot, 2);
      tradeRequest.type = ORDER_TYPE_BUY;
      tradeRequest.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      if(tp > 0)
        {
         tradeRequest.tp = NormalizeDouble(tradeRequest.price + (tp * _Point), _Digits);
        }
      if(sl > 0)
        {
         tradeRequest.sl = NormalizeDouble(tradeRequest.price - (sl * _Point), _Digits);
        }
      if(OrderSend(tradeRequest, tradeResult)) //successfully openend the position
        {
         if(enableAlerts)
           {
            Alert(_Symbol, " Successfully openend BUY POSITION #", tradeResult.order, ", Price: ", tradeResult.price);
           }
         PrintFormat("retcode=%u  deal=%I64u  order=%I64u", tradeResult.retcode, tradeResult.deal, tradeResult.order);
         return(true);
        }
      else
        {
         if(enableAlerts)
           {
            Alert(_Symbol, " ERROR opening a BUY POSITION at: ", SymbolInfoDouble(_Symbol, SYMBOL_ASK));
           }
         PrintFormat("ERROR: Opening a BUY POSITION: ErrorCode = %d",GetLastError());//OrderSend failed, output the error code
         return(false);
        }
     }

   if(positionType == POSITION_TYPE_SELL)
     {
      if(buyPositionsVol > volumeLot && AccountInfoDouble(ACCOUNT_MARGIN_LEVEL) > 200)
        {
         volumeLot = NormalizeDouble((buyPositionsVol + volumeLot), 2);
        }
      if(volumeLot < 0.01)
        {
         volumeLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
        }
      if(volumeLot > SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX))
        {
         volumeLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
        }
      tradeRequest.volume = NormalizeDouble(volumeLot, 2);
      tradeRequest.type = ORDER_TYPE_SELL;
      tradeRequest.price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      if(tp > 0)
        {
         tradeRequest.tp = NormalizeDouble(tradeRequest.price - (tp * _Point), _Digits);
        }
      if(sl > 0)
        {
         tradeRequest.sl = NormalizeDouble(tradeRequest.price + (sl * _Point), _Digits);
        }
      if(OrderSend(tradeRequest, tradeResult)) //successfully openend the position
        {
         if(enableAlerts)
           {
            Alert(_Symbol, " Successfully openend SELL POSITION #", tradeResult.order, ", Price: ", tradeResult.price);
           }
         PrintFormat("retcode=%u  deal=%I64u  order=%I64u", tradeResult.retcode, tradeResult.deal, tradeResult.order);
         return(true);
        }
      else
        {
         if(enableAlerts)
           {
            Alert(_Symbol, " ERROR opening a SELL POSITION at: ", SymbolInfoDouble(_Symbol, SYMBOL_ASK));
           }
         PrintFormat("ERROR: Opening a SELL POSITION: ErrorCode = %d",GetLastError());//OrderSend failed, output the error code
         return(false);
        }
     }
   return(false);
  }

Função PositionFound:

Essa função verifica se a posição especificada existe, para que o Expert Advisor não abra várias posições duplicadas na mesma vela.

bool PositionFound(string symbol, int positionType, string positionComment)
  {
   if(PositionsTotal() > 0)
     {
      ulong positionTicket;
      int totalPositions = PositionsTotal();
      //scan all the open positions
      for(int x = totalPositions - 1; x >= 0; x--)
        {
         positionTicket = PositionGetTicket(x);//gain access to other position properties by selecting the ticket
         if(
            PositionGetInteger(POSITION_MAGIC) == magicNumber && PositionGetString(POSITION_SYMBOL) == symbol &&
            PositionGetInteger(POSITION_TYPE) == positionType && PositionGetString(POSITION_COMMENT) == positionComment
         )
           {
            return(true);//a similar position exists, don't open another position on this candle
            break;
           }
        }
     }
   return(false);
  }


Agora que definimos nossas funções personalizadas, vamos chamá-las para executar as tarefas planejadas.

  • Coloque e chame a função GetInit na função OnInit. 

int OnInit()
  {
//---
   if(GetInit() <= 0)
     {
      return(INIT_FAILED);
     }
//---
   return(INIT_SUCCEEDED);
  }

  • Coloque e chame a função GetDeinit na função OnDeinit. 

void OnDeinit(const int reason)
  {
//---
   GetDeinit();
  }

  • Coloque e chame as seguintes funções na função OnTick na ordem apropriada. Como algumas funções alteram variáveis globais das quais outras funções dependem para tomar decisões chave, garantimos que sejam chamadas primeiro, assegurando que os dados sejam processados e atualizados antes de serem acessados por outras funções. 

void OnTick()
  {
//---
   GetEma();
   GetPositionsData();
   if(TradingIsAllowed())
     {
      TradeNow();
      ManageProfitAndLoss();
     }
   PrintOnChart();
  }

Quando o código do Expert Advisor estiver pronto, salve e compile. Isso permitirá que você acesse seu Expert Advisor diretamente do terminal de negociação. O código completo está anexado abaixo.


Testando nosso Expert Advisor no testador de estratégias

É fundamental garantir que o Expert Advisor funcione conforme o planejado. Podemos conseguir isso, carregando-o em um gráfico de símbolo ativo e negociando em uma conta demo ou usando o testador de estratégias para uma avaliação abrangente. Embora você possa testá-lo em uma conta demo, aqui usaremos o testador de estratégias para avaliar sua eficácia.

Estas são as configurações que aplicaremos no testador de estratégias:

  • Corretora: MetaQuotes-Demo (criado automaticamente ao instalar o MetaTrader 5)

  • Símbolo: EURUSD

  • Período de teste (Intervalo): Último ano (novembro de 2022 - novembro de 2023)

  • Modelagem: Cada tick baseado em ticks reais

  • Depósito inicial: 10.000 USD

  • Alavancagem: 1:100

Configurações do testador de estratégias PriceActionEA


Configurações do testador de estratégias PriceActionEA


Analisando os resultados do nosso backtest, vemos que nosso Expert Advisor não apenas gerou lucro, mas também manteve um rebaixamento surpreendentemente baixo. Essa estratégia é promissora e pode ser ainda mais modificada e otimizada para alcançar resultados melhores, especialmente quando aplicada simultaneamente a vários símbolos.

Resultados do backtest PriceActionEMA

Resultados do backtest PriceActionEMA

Resultados do backtest PriceActionEMA


Considerações finais

Mesmo programadores iniciantes em MQL5 não terão dificuldade em entender o código procedural do Expert Advisor que acabamos de criar. Essa simplicidade surge da natureza clara e direta da programação procedural, especialmente ao usar funções para estruturar o código com base em tarefas específicas e variáveis globais para transmitir dados modificados a várias funções.

Você pode notar que uma desvantagem do código procedural é sua tendência a crescer significativamente à medida que o Expert Advisor se torna mais complexo, tornando-o adequado principalmente para projetos menos complexos. Nos casos em que o projeto é muito complexo, é melhor optar por uma abordagem orientada a objetos.

No nosso próximo artigo, vamos explorar a programação orientada a objetos e converter o código do nosso Expert Advisor procedural baseado em dinâmica de preço para código orientado a objetos. Isso fornecerá uma comparação clara desses paradigmas e uma compreensão mais nítida das diferenças.

Obrigado pela atenção! Desejo a você sucesso no desenvolvimento em MQL5 e na negociação.

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/13771

Arquivos anexados |
PriceActionEMA.mq5 (23.33 KB)
EA de grid-hedge modificado em MQL5 (Parte I): Criando um EA de hedge simples EA de grid-hedge modificado em MQL5 (Parte I): Criando um EA de hedge simples
Criaremos um EA de hedge simples como base para nosso EA Grid-Hedge mais avançado, que será uma mistura de estratégias clássicas de grade e de hedge clássicas. Ao final deste artigo, você saberá como criar uma estratégia de hedge simples e o que as pessoas estão dizendo sobre a lucratividade dessa estratégia.
Buffers de cores em indicadores de vários símbolos e vários períodos Buffers de cores em indicadores de vários símbolos e vários períodos
Neste artigo, analisaremos a estrutura do buffer de indicador em indicadores com vários símbolos e vários períodos e geraremos a exibição dos buffers coloridos desses indicadores no gráfico.
Algoritmos de otimização populacional: simulação de têmpera isotrópica (Simulated Isotropic Annealing, SIA). Parte II Algoritmos de otimização populacional: simulação de têmpera isotrópica (Simulated Isotropic Annealing, SIA). Parte II
A primeira parte do artigo foi dedicada ao conhecido e popular algoritmo de têmpera simulada, onde foram analisadas suas vantagens e descritos detalhadamente os pontos fracos. A segunda parte do artigo é dedicada a uma transformação radical do algoritmo, seu renascimento em um novo algoritmo de otimização, a simulação de têmpera isotrópica, SIA.
Rede neural na prática: Função de reta Rede neural na prática: Função de reta
Neste artigo, vamos passar rapidamente, por alguns métodos para conseguir a função que poderá representar os nossos dados no banco. Não irei me aprofundar em detalhes relacionados ao como usar estatísticas e estudos de probabilidade para interpretar os resultados. Deixo isto como dever de casa, para cada um que realmente deseja se aprofundar, na parte matemática da coisa. De qualquer forma, estudar tais coisas será primordial para que você de fato consiga compreender tudo que envolve estudos de redes neurais. Aqui irei pegar bem leve no tema.