English Русский 中文 Español Deutsch 日本語
preview
Criando um Limitador de Drawdown Diário EA em MQL5

Criando um Limitador de Drawdown Diário EA em MQL5

MetaTrader 5Negociação |
390 2
Kikkih25
Kikkih25

Introdução 

Neste artigo, estamos criando um Expert Advisor (EA) de Limitação de Drawdown Diário para negociação forex em MetaQuotes Language (MQL5) para o MetaTrader 5. O objetivo deste EA é estabelecer um limite diário de retiradas para contas de negociação. O EA analisa fatores como o número total de barras, saldo inicial, hora do dia e saldo diário e verifica se a negociação ocorre com base em condições específicas. Ele também coleta informações como saldo, capital e fundos da plataforma MetaTrader. Este EA foi projetado especificamente para operar com a plataforma de negociação MetaTrader e requer uma conta de negociação para funcionar corretamente.

Esta jornada abordará os seguintes tópicos:

  1. Explicação sobre o Limitador de Drawdown
  2. Criação do EA em MQL5
  3. Conclusão


Explicação sobre o Limitador de Drawdown

Um limitador de drawdown é uma ferramenta usada em negociação e investimentos para gerenciar riscos, limitando perdas potenciais durante um período de drawdown. Um período de drawdown ocorre quando o valor de um ativo ou de um portfólio diminui devido à volatilidade do mercado ou condições econômicas. Durante esse período, o limitador de drawdown ajuda a proteger os investidores de perdas substanciais, vendendo automaticamente todo ou parte do investimento, caso o valor caia abaixo de um nível estabelecido. Essa ferramenta visa reduzir as perdas possíveis e proteger o capital do investidor. Contas de futuros gerenciados e outros veículos de investimento comumente utilizam limitadores de drawdown para controlar riscos e se proteger contra quedas significativas no mercado. 

Os traders enfrentam dificuldades ao controlar o drawdown ao negociar em contas financiadas. Um limitador de drawdown diário foi feito para eles. As empresas de prop trading geralmente estabelecem uma regra chamada 'Trader Daily Drawdown', e caso essa regra não seja respeitada, o trader é desqualificado. O Limitador de Drawdown ajuda os traders em:

  1. Acompanhamento do Drawdown da Conta
  2. Alertar o Trader Quando Ele ou Ela Estiver Tomando Negócios de Alto Risco
  3. Acompanhamento do Drawdown Diário do Trader
  4. Prevenir o Trader de Overtrading Limitar as Posições Abertas 


Criação do EA em MQL5 

A função essencial do Expert Advisor é monitorar todas as atividades na conta, seja uma negociação manual ou automatizada por outro Expert Advisor. O trader precisa adicionar um gráfico, e o EA tomará o controle. Os traders notarão 'Semáforos' em sua tela. A funcionalidade 'Semáforos' informará os traders sobre os quatro pontos-chave mencionados acima de uma maneira gráfica simples. Os comentários do EA podem ser ocultados e personalizados para combinar com as cores e fontes preferidas dos traders. Com um clique, a página de detalhes pode ser facilmente ocultada para ganhar espaço nos gráficos. A posição e o estilo dos semáforos podem ser altamente personalizáveis para combinar com o estilo de gráficos do trader.

Precisamos abrir posições de negociação. A maneira mais fácil de abrir posições é incluir uma instância de negociação, geralmente conseguida pela inclusão de outro arquivo dedicado à abertura de posições. Usamos a diretiva include para incluir a biblioteca de negociação, que contém funções para operações de negociação. Primeiro, usamos os sinais de menor e maior para indicar que o arquivo que queremos incluir está contido na pasta include e fornecemos a pasta Trade, seguida por uma barra normal ou barra invertida, e então o nome do arquivo alvo, neste caso, "Trade.MQH". cTrade é uma classe para manipulação de operações de negociação, e obj-trade é uma instância dessa classe, geralmente um objeto ponteiro criado a partir da classe cTrade para fornecer acesso às variáveis membros da classe.

#include <Trade/Trade.mqh>
CTrade obj-Trade;

Depois, precisamos de alguma lógica de controle para gerar sinais de abertura de posições. No nosso caso, a função OnTick() verifica se a variável isTradeAllowed é verdadeira. Caso seja, a função checkDailyProfit() é chamada, sugerindo que o propósito da função OnTick() é verificar o lucro diário e permitir ou não negociações com base na verificação. As barras monitoram o número total de barras no gráfico, garantindo que a lógica de negociação seja executada apenas uma vez por nova barra, prevenindo múltiplas execuções dentro de uma única barra. Juntas, essas variáveis permitem que o Expert Advisor gere sinais de negociação com base em valores, mantendo o tempo de execução adequado. Como a função não recebe parâmetros, vamos prosseguir para as ações que ela executa conforme a seguir:

  • Ela define a variável total_day_Profit e a inicializa com o valor 0.  
  • Além disso, ela recebe a hora atual e a converte em uma string usando a função TimeToString, armazenando no date.
  • Da mesma forma, calcula a hora inicial do dia, adicionando 1 ao início do dia, e armazena isso em uma variável.
  • Verifica se a hora atual (diária) é menor que o horário. Se for, define o valor de dayTime e calcula o saldo atual usando a função Acc_B, que é armazenado na variável dayBalance.
  • Seleciona os dados históricos do dia usando a função HistorySelect, com os horários de início e fim configurados para o início e fim do dia.
  • Calcula o número total de negócios do dia usando a função HistoryDealsTotal e armazena isso na variável TotalDeals.
  • Além disso, percorre cada transação no histórico e verifica se o tipo de entrada da transação é DEAL_ENTRY_OUT, o que significa que é uma transação de fechamento. Caso seja, calcula o lucro da negociação somando os valores DEAL_PROFIT, DEAL_COMMISSION, DEAL_SWAP e adiciona ao total_day_profit.
  • Calcula o saldo de abertura do dia subtraindo o total_day_Profit do saldo atual da conta usando a função AccountInfoDouble com o parâmetro ACCOUNT-BALANCE.

A função retorna o saldo de abertura calculado como um valor Double.

int totalBars = 0;
double initialBalance = 0;
datetime dayTime = 0;
double dayBalance = 0;
bool isTradeAllowed = true;

Próximo, vamos para a definição das funções. A função parece estar relacionada com informações da conta e retorna diferentes valores com base no nome da função. As funções Acc_B(), Acc_E() e Acc_S() são usadas para recuperar informações sobre o saldo, o patrimônio e a moeda da conta, respectivamente. Essas funções são usadas para monitorar o status financeiro da conta.

double Acc_B(){return AccountInfoDouble(ACCOUNT_BALANCE);}
double Acc_E(){return AccountInfoDouble(ACCOUNT_EQUITY);}
string Acc_S(){return AccountInfoString(ACCOUNT_CURRENCY);}

O código completo para abertura de posições é o seguinte:

#include <Trade/Trade.mqh>
CTrade obj-Trade;

int totalBars = 0;
double initialBalance = 0;
datetime dayTime = 0;
double dayBalance = 0;
bool isTradeAllowed=true;

double Acc_B(){return AccountInfoDouble(ACCOUNT_BALANCE);}
double Acc_E(){return AccountInfoDouble(ACCOUNT_EQUITY);}
string Acc_S(){return AccountInfoString(ACCOUNT_CURRENCY);}

O manipulador de evento OnInit é chamado sempre que o Expert Advisor é inicializado. É a instância que precisamos para inicializar o indicador e criar um texto para exibir os dados do saldo inicial da conta para uma análise posterior. Para inicializar o indicador, usamos a função incorporada para retornar seu createText fornecendo os parâmetros corretos. O objeto de texto é posicionado em determinadas coordenadas e usa cores com um tamanho de fonte. Aqui está uma explicação do que alcançamos com esta função:

  1. Ela busca o saldo inicial da conta com a função Acc_B() e armazena no variável initialBalance.
  2. Isso criará uma caixa de texto com o texto '* PROP FIRM PROGRESS DASHBOARD *' na posição (30,30) na tela, com um tamanho de fonte de 13 e cor azul claro (clrAqual).
  3. Isso criará várias caixas de texto para exibir diferentes mensagens e informações ao usuário. Essas caixas de texto são posicionadas em locais diferentes na tela e têm diferentes tamanhos de fontes e cores.

Aqui, o principal objetivo é criar uma interface de usuário que exiba várias informações relacionadas à gestão de contas e negociações. As caixas de texto são usadas para exibir informações da conta, mensagens e outros dados relevantes para o usuário.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {

   initialBalance = Acc_B();
   
   createText("0","***DAILY DRAWDOWN LIMITER ***",30,30,clrAqua,13);
   createText("00","______________________________________",30,30,clrAqua,13);
   createText("1","DrawDown Limiter is Active.",70,50,clrWhite,11);
   createText("2","Counters will be Reset on Next Day Start.",70,65,clrWhite,10);
   createText("3","From: ",70,80,clrWhite,10);
   createText("4",'Time Here',120,80,clrGray,10);
   createText("5","To: ",70,95,clrWhite,10);
   createText("6",'Time Here',120,95,clrGray,10);
   createText("7",'Current: ',70,110,clrWhite,10);
   createText("8",'Time Here',120,110,clrGray,10);

   createText("9",'ACCOUNT DRAWDOWN ============',70,130,clrPeru,11);
   createText("10",'Account Initial Balance: ',70,145,clrWhite,10);
   createText("11",DoubleToString(initialBalance,2)+" "+Acc_S(),250,145,clrWhite,10);
   createText("12",'Torelated DrawDown: ',70,160,clrWhite,10);
   createText("13","12.00 %",250,160,clrAqua,10);
   createText("14",'Current Account Equity: ',70,175,clrWhite,10);
   createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrWhite,10);
   createText("16",'Current Balance Variation: ',70,190,clrWhite,10);
   createText("17",DoubleToString((Acc_E()-Acc_B())/Acc_B()*100,2)+" %",250,190,clrGray,10);

   createText("18",'DAILY DRAWDOWN ================',70,210,clrPeru,11);
   createText("19",'Starting Balance: ',70,225,clrWhite,10);
   createText("20",DoubleToString(Acc_B(),2)+" "+Acc_S(),270,225,clrWhite,10);
   createText("21",'DrawDowm Maximum Threshold: ',70,240,clrWhite,10);
   createText("22",'5.00 %"+" "+Acc_S(),270,240,clrAqua,10);
   createText("23",'DrawDown Maximum Amount: ',70,255,clrWhite,10);
   createText("24",'-"+DoubleToString(Acc_B()*5/100,2)+' "+Acc_S(),270,255,clrYellow,10);
   createText("25",'Current Closed Daily Profit: ',70,270,clrWhite,10);
   createText("26",'0.00"+" "+Acc_S(),270,270,clrGray,10);
   createText("27",'Current DrawDown Percent: ',70,285,clrWhite,10);
   createText("28",'0.00"+" %",270,285,clrGray,10);

   createText("29",'>>> Initializing The Program, Get Ready To Trade.",70,300,clrYellow,10);
   
   return(INIT_SUCCEEDED);
}

Aqui, a função OnTick é chamada sempre que há um novo tick para o símbolo ao qual o EA está anexado. Para a função checkDailyProfit, é necessário garantir que ela seja implementada corretamente. A variável isTradeAllowed é uma variável booleana que controla se a negociação é permitida. Caso isTradeAllowed seja falsa, ela retorna imediatamente e nenhum código adicional é executado dentro da função OnTick.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){   
   
   checkDailyProfit();
   
   if (!isTradeAllowed) return;
    

Definimos instâncias da quebra de swing. Isso precisa ser feito a cada tick, então fazemos isso sem restrições. Primeiro, declaramos os preços Ask e Bid que usaremos para abrir as posições uma vez que as respectivas condições sejam atendidas. Observe que isso também precisa ser feito a cada tick para obter as cotações de preços mais recentes. Aqui, declaramos as variáveis do tipo double para armazenar os preços recentes e as normalizamos para os dígitos da moeda do símbolo, arredondando o número de ponto flutuante para manter a precisão.

double ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); 
double bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); 

Após definir as instâncias da quebra de swing, agora avançamos para definir uma função chamada 'iBars', que, no nosso caso, recebe dois parâmetros: '_Symbol' e '_Period'. A função 'iBars' retorna um valor inteiro conhecido como 'bars'. Em seguida, verificamos se a variável 'totalBars' é igual ao valor retornado pela função 'iBars'. Caso sejam iguais, a função retorna sem fazer mais nada. Caso contrário, o valor de 'totalBars' é definido como o valor de 'bars' retornado pela função 'iBars'.

   int bars = iBars(_Symbol,_Period);
   if (totalBars == bars) return;
   totalBars = bars;

Agora continuamos a partir da definição da função "iBars"; aqui verificamos se os resultados da chamada da função 'positionsTotal()' são maiores que 1. Caso seja, a função retorna sem fazer mais nada. Caso não seja, o código passa para a próxima linha. A linha 'int number = MathRand()%' parece estar incompleta, pois não há parênteses de fechamento ou ponto e vírgula. Pressupondo que o objetivo seja gerar um número inteiro aleatório, a linha seria completada da seguinte forma: "int number = MathRand()% totalBars;" Essa linha gera um número aleatório entre 0 e o valor de 'totalBars' (inclusive) e o atribui à variável 'number'.

 if (PositionsTotal() > 1) return;
   int number = MathRand()%

O código completo para definição de funções é o seguinte:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){   
   
   checkDailyProfit();
   
   if (!isTradeAllowed) return;
   
   double ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);

   int bars = iBars(_Symbol,_Period);
   if (totalBars == bars) return;
   totalBars = bars;
   
   if (PositionsTotal() > 1) return;
   int number = MathRand()%

Aqui está a explicação dos principais componentes das funções do artigo, como executar negociações, criar rótulos de texto no gráfico e verificar o lucro diário: 

1. Execução de Negócios 

Neste componente, estamos examinando o valor de uma variável chamada 'number' e tomando diferentes ações com base em seu valor.

Se o valor de 'number' for 0, o código aciona um método chamado 'Buy' em um objeto identificado como 'obj-Trade'. Este método exige cinco parâmetros: o primeiro é 0.1, o segundo é a variável '_Symbol', o terceiro é a variável 'ask', o quarto é o valor de 'ask' menos 70 vezes a variável '_Point', e o quinto é o valor de 'ask' mais 70 vezes a mesma variável '_Point'. Isso indica que o código está tentando comprar um ativo por um preço ligeiramente abaixo do preço de venda atual, considerando o spread de compra e venda.

Se o valor de 'number' for 1, o código executa um método chamado 'Sell' no mesmo objeto 'obj-Trade'. Esse método também recebe cinco parâmetros: o primeiro é 0.1, o segundo é a variável '_Symbol', o terceiro é a variável 'bid', o quarto é o valor de 'bid' mais 70 vezes a variável '_Point', e o quinto é o valor de 'bid' menos 70 vezes a mesma variável '_Point'. Isso implica que o código está tentando vender um ativo por um preço ligeiramente mais alto que o preço de compra atual, também considerando o spread de compra e venda. Esse processo avalia o valor de uma variável chamada 'number' e executa diferentes ações com base em seu valor.

if (number == 0){
      obj_Trade.Buy(0.1,_Symbol,ask,ask-70*_Point,ask+70*_Point);
   }
   else if (number == 1){
      obj_Trade.Sell(0.1,_Symbol,bid,bid+70*_Point,bid-70*_Point);
   }

2. Criação de Texto

No código anterior, existe uma função chamada createText que é responsável por criar um objeto de rótulo no gráfico. Para executar essa função, são necessários certos parâmetros, como o nome do objeto (objName), o conteúdo do texto (text), as coordenadas x e y para a colocação do rótulo (x e y), a cor do texto (clrTxt) e o tamanho da fonte (font size). Usando esses parâmetros, a função cria um objeto de rótulo no gráfico, personaliza suas características e atualiza o gráfico. Além disso, um valor booleano é retornado pela função para indicar se a criação do rótulo foi bem-sucedida ou não.


bool createText(string objName,string text,int x, int y,color clrTxt,int fontSize){
   ResetLastError();
   if (!ObjectCreate(0,objName,OBJ_LABEL,0,0,0)){
      Print(__FUNCTION__,": failed to create the Label! Error Code = ",GetLastError());
      return (false);
   }
   ObjectSetInteger(0,objName,OBJPROP_XDISTANCE,x);
   ObjectSetInteger(0,objName,OBJPROP_YDISTANCE,y);
   ObjectSetInteger(0,objName,OBJPROP_CORNER,CORNER_LEFT_UPPER);
   ObjectSetString(0,objName,OBJPROP_TEXT,text);
   ObjectSetInteger(0,objName,OBJPROP_COLOR,clrTxt);
   ObjectSetInteger(0,objName,OBJPROP_FONTSIZE,fontSize);
   
   ChartRedraw(0);
   return (true);
}

3. Verificação do Lucro Diário

Nesta função MQL chamada checkDailyProfit, que calcula o lucro total de um determinado dia. A função não recebe parâmetros e executa os seguintes passos:

  • Ela define a variável total_day_Profit e a inicializa com o valor 0.
  • Obtém a hora atual e a converte em uma string usando a função TimeToString, que é armazenada na variável date.
  • Calcula a hora inicial do dia adicionando 1 ao início do dia e salva isso em uma variável.
  • Verifica se a hora do dia é menor que a hora atual. Se sim, define o valor de dayTime e calcula o saldo atual usando a função Acc_B, que é armazenado na variável dayBalance.
  • Seleciona os dados históricos do dia usando a função HistorySelect, com os horários de início e fim configurados para o início e fim do dia.
  • Calcula o número total de negócios do dia usando a função HistoryDealsTotal e armazena isso na variável TotalDeals.
  • Percorre cada transação no histórico e verifica se o tipo de entrada da transação é DEAL_ENTRY_OUT, o que significa que é uma transação de fechamento. Se sim, calcula o lucro da negociação somando os valores DEAL_PROFIT, DEAL_COMMISSION, DEAL_SWAP e adiciona à variável total_day_profit.
  • Calcula o saldo de abertura do dia subtraindo o total_day_profit do saldo atual da conta usando a função AccountInfoDouble com o parâmetro ACCOUNT_BALANCE.

A função retorna o saldo de abertura calculado como um valor Double.

void checkDailyProfit(){

   double total_day_Profit = 0;
   datetime end = TimeCurrent();
   string sdate = TimeToString(TimeCurrent(),TIME_DATE);
   datetime start = StringToTime(sdate);
   datetime to = start + (1*24*60*60);
   
   if (dayTime < to){
      dayTime = to;
      dayBalance = Acc_B();
   }
   
   HistorySelect(start,end);
   int TotalDeals = HistoryDealsTotal();
   for (int i=0; i<TotalDeals; i++){
      ulong Ticket = HistoryDealGetTicket(i);
      if (HistoryDealGetInteger(Ticket,DEAL_ENTRY)==DEAL_ENTRY_OUT){
         double Latest_Day_Profit = (HistoryDealGetDouble(Ticket,DEAL_PROFIT)
                               +HistoryDealGetDouble(Ticket,DEAL_COMMISSION)
                               +HistoryDealGetDouble(Ticket,DEAL_SWAP));
         total_day_Profit += Latest_Day_Profit;
      }
   }
   double startingBalance = 0;
   startingBalance = AccountInfoDouble(ACCOUNT_BALANCE) - total_day_Profit;
   double daily_profit_or_drawdown = NormalizeDouble((total_day_Profit*100/startingBalance),2);
   string daily_profit_in_Text_Format = "";
   daily_profit_in_Text_Format = DoubleToString(daily_profit_or_drawdown,2)+" %";
   
   //Print(total_day_Profit, " >>> ",daily_profit_in_Text_Format);
   
   createText("4",TimeToString(start),120,80,clrYellow,10);
   createText("6",TimeToString(to),120,95,clrYellow,10);
   createText("8",TimeToString(end),120,110,clrWhite,10);

   if (Acc_E() > initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrLime,10);
      createText("17",DoubleToString((Acc_E()-initialBalance)/initialBalance*100,2)+" %",250,190,clrLime,10);
   }
   else if (Acc_E() < initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrRed,10);
      createText("17",DoubleToString((Acc_E()-initialBalance)/initialBalance*100,2)+" %",250,190,clrRed,10);
   }
   if (Acc_E() == initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrWhite,10);
      createText("17",DoubleToString((Acc_E()-initialBalance)/initialBalance*100,2)+" %",250,190,clrWhite,10);
   }
   
   createText("20",DoubleToString(dayBalance,2)+" "+Acc_S(),270,225,clrWhite,10);
   createText("24","-"+DoubleToString(dayBalance*5/100,2)+" "+Acc_S(),270,255,clrYellow,10);

   if (Acc_B() > dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrLime,10);
      createText("28",daily_profit_in_Text_Format,270,285,clrLime,10);
   }
   else if (Acc_B() < dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrRed,10);
      createText("28",daily_profit_in_Text_Format,270,285,clrRed,10);
   }
   else if (Acc_B() == dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrWhite,10);
      createText("28",daily_profit_in_Text_Format,270,285,clrWhite,10);
   }
   
   if (daily_profit_or_drawdown <= -5.00 || ((Acc_E()-initialBalance)/initialBalance*100) < -12.00){
      createText("29",">>> Maximum Threshold Hit, Can't Trade.",70,300,clrRed,10);
      isTradeAllowed = false;
   }
   else {
      createText("29",">>> Maximum Threshold Not Hit, Can Trade.",70,300,clrL…

O código completo dos principais componentes das funções do artigo é o seguinte:

2;
   if (number == 0){
      obj_Trade.Buy(0.1,_Symbol,ask,ask-70*_Point,ask+70*_Point);
   }
   else if (number == 1){
      obj_Trade.Sell(0.1,_Symbol,bid,bid+70*_Point,bid-70*_Point);
   }
}
//+------------------------------------------------------------------+
bool createText(string objName,string text,int x, int y,color clrTxt,int fontSize){
   ResetLastError();
   if (!ObjectCreate(0,objName,OBJ-LABEL,0,0,0)){
      Print(__FUNCTION__,": failed to create the Label! Error Code = ",GetLastError());
      return (false);
   }
   ObjectSetInteger(0,objName,OBJPROP-XDISTANCE,x);
   ObjectSetInteger(0,objName,OBJPROP-YDISTANCE,y);
   ObjectSetInteger(0,objName,OBJPROP-CORNER,CORNER_LEFT_UPPER);
   ObjectSetString(0,objName,OBJPROP-TEXT,text);
   ObjectSetInteger(0,objName,OBJPROP-COLOR,clrTxt);
   ObjectSetInteger(0,objName,OBJPROP-FONTSIZE,fontSize);
   
   ChartRedraw(0);
   return (true);
}

void checkDailyProfit(){

   double total_day_Profit = 0;
   datetime end = TimeCurrent();
   string sdate = TimeToString(TimeCurrent(),TIME_DATE);
   datetime start = StringToTime(sdate);
   datetime to = start + (1*24*60*60);
   
   if (dayTime < to){
      dayTime = to;
      dayBalance = Acc_B();
   }
   
   HistorySelect(start,end);
   int TotalDeals = HistoryDealsTotal();
   for (int i=0; i<TotalDeals; i++){
      ulong Ticket = HistoryDealGetTicket(i);
      if (HistoryDealGetInteger(Ticket,DEAL_ENTRY)==DEAL_ENTRY_OUT){
         double Latest_Day_Profit = (HistoryDealGetDouble(Ticket,DEAL_PROFIT)
                               +HistoryDealGetDouble(Ticket,DEAL_COMMISSION)
                               +HistoryDealGetDouble(Ticket,DEAL_SWAP));
         total_day_Profit += Latest_Day_Profit;
      }
   }
   double startingBalance = 0;
   startingBalance = AccountInfoDouble(ACCOUNT-BALANCE) - total_day_Profit;
   double daily_profit_or_drawdown = NormalizeDouble((total_day_Profit*100/startingBalance),2);
   string daily_profit_in_Text_Format = "";
   daily_profit_in_Text_Format = DoubleToString(daily_profit_or_drawdown,2)+" %";
   
   //Print(total_day_Profit, " >>> ",daily_profit_in_Text_Format);
   
   createText("4",TimeToString(start),120,80,clrYellow,10);
   createText("6",TimeToString(to),120,95,clrYellow,10);
   createText("8",TimeToString(end),120,110,clrWhite,10);

   if (Acc_E() > initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrLime,10);
      createText("17",DoubleToString((Acc_E()-initialBalance)/initialBalance*100,2)+" %",250,190,clrLime,10);
   }
   else if (Acc_E() < initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrRed,10);
      createText("17",DoubleToString((Acc_E()-initialBalance)/initialBalance*100,2)+" %",250,190,clrRed,10);
   }
   if (Acc_E() == initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrWhite,10);
      createText("17",DoubleToString((Acc_E()-initialBalance)/initialBalance*100,2)+" %",250,190,clrWhite,10);
   }
   
   createText("20",DoubleToString(dayBalance,2)+" "+Acc_S(),270,225,clrWhite,10);
   createText("24","-"+DoubleToString(dayBalance*5/100,2)+" "+Acc_S(),270,255,clrYellow,10);

   if (Acc_B() > dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrLime,10);
      createText("28",daily_profit_in_Text_Format,270,285,clrLime,10);
   }
   else if (Acc_B() < dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrRed,10);
      createText("28",daily_profit_in_Text_Format,270,285,clrRed,10);
   }
   else if (Acc_B() == dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrWhite,10);
      createText("28",daily_profit_in_Text_Format,270,285,clrWhite,10);
   }
   
   if (daily_profit_or_drawdown <= -5.00 || ((Acc_E()-initialBalance)/initialBalance*100) < -12.00){
      createText("29",">>> Maximum Threshold Hit, Can't Trade.",70,300,clrRed,10);
      isTradeAllowed = false;
   }
   else {
      createText("29",">>> Maximum Threshold Not Hit, Can Trade.",70,300,clrRed);

A seguir, temos o que obtemos.

Exemplo de uma lógica de limiar distante.

LIMIAR DISTANTE

Exemplo de uma lógica de limiar próximo.

LIMIAR PRÓXIMO

Exemplo de uma lógica de limiar atingido:

LIMIAR ATINGIDO

O código completo para criar um limitador de drawdown é o seguinte:

//+------------------------------------------------------------------+
//|                                       Daily Drawdown Limiter.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
CTrade obj_Trade;

int totalBars = 0;
double initialBalance = 0;
double dayBalance = 0;
datetime dayTime = 0;
bool isTradeAllowed = true;

// Functions to get account balance, equity, and currency
double Acc_B() {return AccountInfoDouble(ACCOUNT_BALANCE);}
double Acc_E() {return AccountInfoDouble(ACCOUNT_EQUITY);}
string Acc_S() {return AccountInfoString(ACCOUNT_CURRENCY);}

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   // Initialize initial balance
   initialBalance = Acc_B();
   
   // Create dashboard texts
   createText("0","*** Daily Drawdown Limiter ***",30,30,clrBlack,13);
   createText("00","______________________________________",30,30,clrBlack,13);
   createText("1","DrawDown Limiter is Active.",70,50,clrBlack,11);
   createText("2","Counters will be reset on Next Day Start.",70,65,clrBlack,10);
   createText("3","From: ",70,80,clrBlack,10);
   createText("4","Time Here",120,80,clrGray,10);
   createText("5","To: ",70,95,clrBlack,10);
   createText("6","Time Here",120,95,clrGray,10);
   createText("7","Current: ",70,110,clrBlack,10);
   createText("8","Time Here",120,110,clrGray,10);

   createText("9","ACCOUNT DRAWDOWN ============",70,130,clrPeru,11);
   createText("10","Account Initial Balance: ",70,145,clrBlack,10);
   createText("11",DoubleToString(initialBalance,2)+" "+Acc_S(),250,145,clrBlack,10);
   createText("12","Tolerated DrawDown: ",70,160,clrBlack,10);
   createText("13","12.00 %",250,160,clrBlack,10);
   createText("14","Current Account Equity: ",70,175,clrBlack,10);
   createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrBlack,10);
   createText("16","Current Balance Variation: ",70,190,clrBlack,10);
   createText("17",DoubleToString((Acc_E()-Acc_B())/Acc_B()*100,2)+" %",250,190,clrGray,10);

   createText("18","DAILY DRAWDOWN ================",70,210,clrPeru,11);
   createText("19","Starting Balance: ",70,225,clrBlack,10);
   createText("20",DoubleToString(Acc_B(),2)+" "+Acc_S(),270,225,clrBlack,10);
   createText("21","DrawDown Maximum Threshold: ",70,240,clrBlack,10);
   createText("22","5.00 %",270,240,clrBlack,10);
   createText("23","DrawDown Maximum Amount: ",70,255,clrBlack,10);
   createText("24","-"+DoubleToString((Acc_B()*5/100),2)+" "+Acc_S(),270,255,clrBlue,10);
   createText("25","Current Closed Daily Profit: ",70,270,clrBlack,10);
   createText("26","0.00"+" "+Acc_S(),270,270,clrGray,10);
   createText("27","Current DrawDown Percent: ",70,285,clrBlack,10);
   createText("28","0.00 %",270,285,clrGray,10);
   createText("29",">>> Initializing The Program, Get Ready To Trade.",70,300,clrBlue,10);

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   // Deinitialization code here (if needed)
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // Check daily profit and drawdown
   checkDailyProfit();
   
   // If trading is not allowed, exit function
   if (!isTradeAllowed) return;
   
   // Get current ask and bid prices
   double ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   
   // Check for new bar
   int bars = iBars(_Symbol,_Period);
   if (totalBars == bars) return;
   totalBars = bars;
   
   // If more than one position, exit function
   if (PositionsTotal() > 1) return;
   
   // Random trade decision
   int number = MathRand()%2;
   Print(number);
   
   if (number == 0){
      obj_Trade.Buy(1,_Symbol,ask,ask-70*_Point,ask+70*_Point);
   }
   else if (number == 1){
      obj_Trade.Sell(1,_Symbol,bid,bid+70*_Point,bid-70*_Point);
   }
  }
//+------------------------------------------------------------------+
//| Check daily profit and drawdown                                  |
//+------------------------------------------------------------------+
void checkDailyProfit() {
   
   double total_day_Profit = 0.0;
   datetime end = TimeCurrent();
   string sdate = TimeToString (TimeCurrent(), TIME_DATE);
   datetime start = StringToTime(sdate);
   datetime to = start + (1*24*60*60);
   
   // Reset daily balance and time at start of new day
   if (dayTime < to){
      dayTime = to;
      dayBalance = Acc_B();
   }

   // Calculate total daily profit
   HistorySelect(start,end);
   int TotalDeals = HistoryDealsTotal();

   for(int i = 0; i < TotalDeals; i++){
      ulong Ticket = HistoryDealGetTicket(i);
      if(HistoryDealGetInteger(Ticket,DEAL_ENTRY) == DEAL_ENTRY_OUT){
         double Latest_Day_Profit = (HistoryDealGetDouble(Ticket,DEAL_PROFIT)
                                    + HistoryDealGetDouble(Ticket,DEAL_COMMISSION)
                                    + HistoryDealGetDouble(Ticket,DEAL_SWAP));
         total_day_Profit += Latest_Day_Profit;
      }
   }
   
   double startingBalance = 0.0;
   startingBalance = AccountInfoDouble(ACCOUNT_BALANCE) - total_day_Profit;
   string day_profit_in_TextFormat = "";
   double daily_Profit_or_Drawdown = NormalizeDouble(((total_day_Profit) * 100/startingBalance),2);
   day_profit_in_TextFormat = DoubleToString(daily_Profit_or_Drawdown,2) + " %";
      
   // Update dashboard texts with new data
   createText("4",TimeToString(start),120,80,clrBlue,10);
   createText("6",TimeToString(to),120,95,clrBlue,10);
   createText("8",TimeToString(end),120,110,clrBlack,10);

   createText("11",DoubleToString(initialBalance,2)+" "+Acc_S(),250,145,clrBlack,10);
   if (Acc_E() > initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrMediumBlue,10);
      createText("17",DoubleToString(((Acc_E()-initialBalance)/initialBalance)*100,2)+" %",250,190,clrMediumBlue,10);
   }
   else if (Acc_E() < initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrRed,10);
      createText("17",DoubleToString(((Acc_E()-initialBalance)/initialBalance)*100,2)+" %",250,190,clrRed,10);
   }
   else if (Acc_E() == initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrBlack,10);
      createText("17",DoubleToString(((Acc_E()-initialBalance)/initialBalance)*100,2)+" %",250,190,clrBlack,10);
   }

   createText("20",DoubleToString(dayBalance,2)+" "+Acc_S(),270,225,clrBlack,10);
   createText("24","-"+DoubleToString((dayBalance*5/100),2)+" "+Acc_S(),270,255,clrBlue,10);
   if (Acc_B() > dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrMediumBlue,10);
      createText("28",day_profit_in_TextFormat,270,285,clrMediumBlue,10);
   }
   else if (Acc_B() < dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrRed,10);
      createText("28",day_profit_in_TextFormat,270,285,clrRed,10);
   }
   else if (Acc_B() == dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrBlack,10);
      createText("28",day_profit_in_TextFormat,270,285,clrBlack,10);
   }
   
   // Check if drawdown limits are hit and update trading permission
   if (daily_Profit_or_Drawdown <= -5.00 ||((Acc_E()-initialBalance)/initialBalance)*100 < -12.00){
      createText("29",">>> Max ThreshHold Hit, Can't Trade.",70,300,clrRed,10);
      isTradeAllowed = false;
   }
   else {
      createText("29",">>> Max ThresHold Not Hit, Can Trade.",70,300,clrMediumBlue,10);
      isTradeAllowed = true;
   }
}

//+------------------------------------------------------------------+
//| Create text label on the chart                                   |
//+------------------------------------------------------------------+
bool createText(string objName, string text, int x, int y, color clrTxt,int fontSize) {
 ResetLastError();
     if (!ObjectCreate(0,objName,OBJ_LABEL,0,0,0)){
        Print(__FUNCTION__,": failed to create the Label! Error code = ", GetLastError());
        return(false);
     }

   ObjectSetInteger(0,objName,OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0,objName,OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0,objName,OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetString(0,objName,OBJPROP_TEXT, text);
   ObjectSetInteger(0,objName,OBJPROP_FONTSIZE, fontSize);
   //ObjectSetString(0,objName,OBJPROP_FONT, "Calibri");
   ObjectSetInteger(0,objName,OBJPROP_COLOR, clrTxt);
   
   ChartRedraw(0);
   
   return(true); 
}

Saúde para nós! Agora criamos um limite diário de drawdown para o Expert Advisor de Trading Forex, baseado no estabelecimento de um limite diário de retirada para as contas de negociação.


Conclusão

Este artigo monitora as atividades de negociação e atualiza o gráfico com rótulos de texto relevantes que indicam o status da negociação, lucro e permissão para negociar com base em certos limiares. Usar funções para realizar operações e atualizar rótulos de texto no gráfico ajuda a organizar o artigo e simplificar a manutenção. Nós analisamos os passos básicos que precisam ser implementados para a automação da famosa estratégia de limitador de drawdown diário para negociação Forex em MQL5. Fornecemos a definição básica e a descrição da estratégia e mostramos como ela pode ser criada em MQL5. Os traders podem usar o conhecimento mostrado para desenvolver um sistema mais complexo de limitador de drawdown diário que pode, posteriormente, ser otimizado para produzir melhores resultados no final.

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

Arquivos anexados |
Últimos Comentários | Ir para discussão (2)
atesz5870
atesz5870 | 10 jul. 2024 em 13:29
Muito bom!
clueboard
clueboard | 11 jul. 2024 em 09:40

Parece que você acabou de usar o ForexAlgo-Trader(YouTube) sem dar a ele nenhum crédito e reivindicando o trabalho dele como seu. Pelo vídeo dele aqui, você pode ver que esse código é dele, linha por linha, variável por variável.

Pelo menos, dê o crédito onde ele é devido. Sim, o código é de código aberto, mas não há nada de errado em dar crédito onde ele é merecido.

Eigenvetores e autovalores: Análise exploratória de dados no MetaTrader 5 Eigenvetores e autovalores: Análise exploratória de dados no MetaTrader 5
Neste artigo, exploramos diferentes maneiras pelas quais os eigenvetores e os autovalores podem ser aplicados na análise exploratória de dados para revelar relacionamentos únicos nos dados.
Do básico ao intermediário: Definições (II) Do básico ao intermediário: Definições (II)
Neste artigo iremos ver e explorar um pouco mais sobre a diretiva #define. Só que agora com o foco voltado para a segunda forma de utilização desta diretiva. Ou seja, a criação de macros. Como sei que este material pode vir a ser um pouco complicado no começo. Procurei utilizar uma aplicação que já vem sendo explorada a algum tempo. Então espero que se divirtam com o conteúdo deste artigo.
Como Integrar o Conceito de Smart Money (BOS) Junto com o Indicador RSI em um EA Como Integrar o Conceito de Smart Money (BOS) Junto com o Indicador RSI em um EA
Conceito de Smart Money (Break Of Structure) acoplado com o Indicador RSI para tomar decisões informadas de negociação automatizada com base na estrutura do mercado.
Desenvolvendo um sistema de Replay (Parte 77): Um novo Chart Trade (IV) Desenvolvendo um sistema de Replay (Parte 77): Um novo Chart Trade (IV)
Neste artigo, explicarei alguns detalhes e cuidados que você teve tomar quando for criar um protocolo de comunicação. São coisas bem básicas e simples. Não irei de fato pegar pesado neste artigo. Mas é preciso que você entenda o conteúdo deste artigo para entender o que acontecerá no receptor.