English Русский 中文 Deutsch 日本語 Português
preview
Creación de un EA limitador de reducción diaria en MQL5

Creación de un EA limitador de reducción diaria en MQL5

MetaTrader 5Trading |
402 2
Kikkih25
Kikkih25

Introducción 

En este artículo, estamos creando un asesor experto en trading de divisas (EA) con limitador de reducción diaria en lenguaje MetaQuotes (MQL5) para MetaTrader 5. El objetivo de este EA es establecer un límite de retiro diario para las cuentas comerciales. El EA analiza factores como el recuento total de barras, el saldo inicial, la hora del día y el saldo diario y verifica si las transacciones se realizan en función de condiciones específicas. También recopila información como saldo, capital y fondos de la plataforma MetaTrader. Este EA está diseñado específicamente para operar con la plataforma de negociación MetaTrader y necesita una cuenta de negociación para su correcto funcionamiento.

Este viaje cubrirá los siguientes temas:

  1. Explicación del limitador de reducción
  2. Creación del EA en MQL5
  3. Conclusión


Explicación del limitador de reducción

Un limitador de reducción es una herramienta utilizada en el comercio y la inversión para gestionar el riesgo al limitar las pérdidas potenciales durante un período de reducción. Un período de reducción ocurre cuando el valor de un activo o una cartera cae debido a la volatilidad del mercado o las condiciones económicas. Durante este período, el limitador de reducción ayuda a proteger a los inversores de pérdidas sustanciales vendiendo automáticamente toda o parte de la inversión si el valor cae por debajo de un nivel establecido. Esta herramienta tiene como objetivo reducir posibles pérdidas y salvaguardar el capital del inversor. Las cuentas de futuros gestionadas y otros vehículos de inversión suelen utilizar limitadores de reducción para controlar el riesgo y protegerse contra caídas significativas del mercado. 

Los traders tienen dificultades para controlar sus pérdidas cuando operan con cuentas financiadas. Un limitador de reducción diaria está pensado para ellos. Las empresas 'Prop firms' generalmente establecen una regla llamada 'Reducción diaria del trader' y, si no se respeta, el trader queda descalificado. El limitador de reducción ayuda a los operadores a:

  1. Seguimiento de la reducción de la cuenta
  2. Alertar al comerciante cuando esté realizando operaciones de alto riesgo
  3. Seguimiento de la caída diaria del trader
  4. Evite que el comerciante opere en exceso limitando las posiciones abiertas 


Creación del EA en MQL5 

La función esencial del Asesor Experto es monitorear todas las actividades en la cuenta, ya sea una operación manual o automatizada por otro Asesor Experto. El comerciante debe agregar un gráfico y tomará el control. Los comerciantes verán 'semáforos' en su pantalla. Dicha función informará a los traders sobre las cuatro claves mencionadas anteriormente de una manera gráfica sencilla. Los comentarios del EA se pueden ocultar y personalizar para que coincidan con sus colores y fuentes preferidos. Con un solo clic, la página de detalles se puede ocultar simplemente para ganar espacio en los gráficos. La posición y el estilo de los semáforos se pueden personalizar enormemente para que coincidan con el estilo de su gráfico.

Necesitamos abrir posiciones comerciales. La forma más sencilla de abrir posiciones es incluir una instancia comercial, lo que generalmente se logra mediante la inclusión de otro archivo dedicado a abrir posiciones. Utilizamos la directiva '#include' para incluir la biblioteca comercial, que contiene funciones para operaciones comerciales. Primero, usamos los corchetes angulares para indicar que el archivo que queremos incluir está contenido en la carpeta de inclusión para proporcionar una carpeta comercial, seguido de una barra o barra invertida normal y luego el nombre del archivo de destino, en este caso, 'Trade.mqh'. cTrade es una clase para manejar las operaciones comerciales, y 'obj-trade' es una instancia de esta clase, normalmente un objeto puntero creado a partir de la clase cTrade para proporcionar acceso a las variables miembro de la clase.

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

Después necesitamos cierta lógica de control para generar señales para abrir posiciones. En nuestro caso, la función OnTick() verifica si la variable 'isTradeAllowed' es verdadera. En caso de ser así, se llama a la función checkDailyProfit(), lo que sugiere que el propósito de la función OnTick() es verificar la ganancia diaria y potencialmente permitir o no permitir transacciones basadas en la verificación. Las barras llevan la cuenta del número total de barras del gráfico, lo que garantiza que la lógica de negociación sólo se ejecute una vez por cada nueva barra, evitando ejecuciones múltiples dentro de una misma barra. En conjunto, estas variables permiten al Asesor Experto generar señales de negociación basadas en valores, manteniendo al mismo tiempo un calendario de ejecución adecuado. Dado que la función no toma parámetros, procedamos a las acciones que realiza de la siguiente manera:

  • Define la variable total_day_Profit y la inicializa a 0.  
  • Además, recibe la hora actual y la convierte en una cadena utilizando la función TimeToString almacenada en la variable de fecha.
  • Asimismo, calcula la hora inicial del día sumando 1 al principio del día y la guarda en una variable.
  • Comprueba si la hora actual (diurna) es menor que la hora. Si es así, establece el valor dayTime y calcula el saldo actual utilizando la función Acc_B, se almacena en la variable dayBalance.
  • Selecciona los datos históricos del día utilizando la función de selección de históricos, con las horas de inicio y fin fijadas al principio y al final del día.
  • Calcula el número total de operaciones del día utilizando la función HistoryDealsTotal y lo almacena en la variable TotalDeals.
  • No sólo eso, sino que recorre cada transacción del historial y comprueba si el tipo de entrada de la transacción es DEAL_ENTRY_OUT, lo que significa que se trata de una transacción de cierre. Si es así, calcula el beneficio de la negociación sumando los valores DEAL_PROFIT, DEAL_COMMISSION DEAL_SWAP y lo añade a la variable total_day_profit.
  • Calcula el saldo inicial del día restando el total_day_Profit del saldo actual de la cuenta utilizando la función AccountInfoDouble con el parámetro ACCOUNT-BALANCE.

La función devuelve el saldo inicial calculado como un valor Double.

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

A continuación, pasamos a la definición de funciones. La función parece estar relacionada con la información de la cuenta y devuelve diferentes valores basados en el nombre de la función. Las funciones Acc_B(), Acc_E() y Acc_S() se utilizan para recuperar información sobre el saldo de la cuenta, el capital y la divisa, respectivamente. Estas funciones se utilizan para supervisar el estado financiero de la cuenta.

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

El código completo de los puestos vacantes es el siguiente:

#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);}

El manejador de eventos Onlnit es llamado cada vez que se inicializa el Asesor Experto. Es la instancia que necesitamos para inicializar el indicador y crear texto para mostrar los datos del saldo inicial de la cuenta para su posterior análisis. Para inicializar el indicador, utilizamos la función incorporada para devolver su createText proporcionando los parámetros correctos. El objeto de texto se sitúa en unas coordenadas determinadas y utiliza colores con un tamaño de fuente. He aquí un desglose de lo que conseguimos con esta función:

  1. Busca el saldo inicial de la cuenta con la función Acc_B() y lo almacena en la variable 'initialBalance'.
  2. Esto creará una caja de texto con el texto '* PROP FIRM PROGRESS DASHBOARD *' en la posición (30,30 en la pantalla, con un tamaño de fuente de 13 y un color azul claro (clrAqual).
  3. Esto creará varios cuadros de texto para mostrar diferentes mensajes e información al usuario. Estos cuadros de texto están situados en distintos lugares de la pantalla y tienen distintos tamaños y colores de letra.

En este caso, el objetivo principal es crear una interfaz de usuario que muestre diversa información relacionada con la gestión de cuentas y la negociación. Los cuadros de texto se utilizan para mostrar información sobre la cuenta, mensajes y otros datos relevantes para el usuario.

//+------------------------------------------------------------------+
//| 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);
}

Aquí se llama a la función OnTick cada vez que hay un nuevo tick para el símbolo al que está vinculado el EA. Para la función checkDailyProfit, hay que asegurarse de que se implementa correctamente. isTradeAllowed es una variable booleana que controla si la negociación está permitida. En caso de que isTradeAllowed sea falso, retorna inmediatamente, y no se ejecuta más código dentro de la función OnTick.

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

Sólo definimos instancias del swing de desglose. Esto debe hacerse en cada tick, así que lo hacemos sin restricciones. Primero declaramos los precios Ask y Bid que utilizaremos para abrir las posiciones una vez que se cumplan las condiciones respectivas. Tenga en cuenta que esto también debe hacerse en cada tick para que obtengamos las cotizaciones de precios más recientes. Aquí, declaramos las variables de tipo de datos double para almacenar los precios recientes y los normalizamos a los dígitos de la moneda símbolo redondeando el número de punto flotante para mantener la precisión.

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

Una vez definidas las instancias del swing de desglose, pasamos a definir una función llamada 'iBars' que en nuestro caso toma dos parámetros: '_Symbol' y '_Period. El 'iBars' devuelve un valor entero conocido como 'bars'. A continuación, comprobamos si la variable 'totalBars' es igual al valor devuelto por la función 'iBars'. En caso de que sean iguales, la función vuelve sin hacer nada más. En caso de que no sean iguales, el valor de 'totalBars' se establece en el valor de 'bars' devuelto por la función 'iBars'.

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

Continuamos ahora desde la definición de la función «iBars»; aquí comprobamos si los resultados de llamar a la función 'positionsTotal()' son mayores que 1. En caso afirmativo, la función vuelve sin hacer nada más. En caso contrario, el código pasa a la línea siguiente. La línea 'int number = MathRand()%' parece estar incompleta porque no hay paréntesis de cierre ni punto y coma. Suponiendo que el objetivo es generar un número entero aleatorio, la línea se completaría de la siguiente manera: «int number = MathRand()% totalBars;' Esta línea genera un aleatorio entre 0 y el valor de 'totalBars' (inclusive), y lo asigna a la variable 'number'.

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

El código completo de las funciones definidas es el siguiente:

//+------------------------------------------------------------------+
//| 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()%

He aquí el desglose de los componentes clave de las funciones del artículo, como la colocación de operaciones, la creación de etiquetas de texto en el gráfico y la comprobación del beneficio diario: 

1. Ejecución de operaciones 

En este componente, examinamos el valor de una variable llamada 'number' y tomamos diferentes acciones en función de su valor.

Si 'number' es 0, el código activa un método llamado 'Buy' en un objeto identificado como 'obj-Trade'. Este método requiere cinco parámetros: el primero es 0,1, el segundo es la variable '_Symbol', el tercero es la variable 'ask', el cuarto es el valor 'ask' menos 70 veces la variable '_Point', y el quinto es el valor 'ask' más 70 veces la misma variable '_Point'. Esto indica que el código está intentando comprar un activo por un precio ligeramente inferior al precio de compra actual, teniendo en cuenta el diferencial entre la oferta y la demanda.

Si el 'número' es 1, el código ejecuta un método llamado 'Sell' en el mismo objeto 'obj-Trade'. Este método también toma cinco parámetros: el primero es 0,1, el segundo es la variable '_Symbol', el tercero es la variable 'bid', el cuarto es el valor de 'bid' más 70 veces la variable '_Point', y el quinto es el valor de 'bid' menos 70 veces la misma variable '_Point'. Esto implica que el código está intentando vender un activo por un precio ligeramente superior al precio de oferta actual, teniendo en cuenta también el diferencial de precios entre la oferta y la demanda. Evalúa el valor de una variable denominada «number» y realiza diferentes acciones en función de su 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. Creación de textos

En nuestro código anterior, existe una función llamada createText que es responsable de crear un objeto etiqueta en un gráfico. Para ejecutar esta función, se requieren ciertos parámetros como el nombre del objeto (objName), el contenido del texto (text), las coordenadas x e y para la colocación de la etiqueta (x e y), el color del texto (clrTxt) y el tamaño de la fuente (font size). Utilizando estas entradas, la función crea un objeto etiqueta en el gráfico, personaliza sus características y actualiza el gráfico. Además, la función devuelve un valor booleano que indica si la etiqueta se ha creado correctamente o no.


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. Comprobación diaria de beneficios

Función MQL llamada checkDailyProfit que calcula el beneficio total para un día determinado. La función no toma parámetros y realiza los siguientes pasos:

  • Define la variable total_day_profit y la inicializa a 0.
  • Obtiene la hora actual y la convierte en string utilizando la función TimeToString, que se almacena en la variable date.
  • Calcula la hora inicial del día sumando 1 al principio del día y guardándolo en una variable.
  • Comprueba si la hora diaria es menor que la hora actual. En caso afirmativo, establece dayTime y calcula el saldo actual utilizando la función Acc_B que se almacena en la variable dayBalance.
  • Selecciona los datos históricos del día utilizando la función HistorySelect, con las horas de inicio y fin fijadas al principio y al final del día.
  • Calcula el número total de operaciones del día utilizando la función HistoryDealsTotal y lo almacena en la variable TotalDeals.
  • Examina la transacción en el historial y comprueba si el tipo de entrada de la transacción es DEAL_ENTRY_OUT, lo que significa que se trata de una transacción de cierre. En caso afirmativo, calcula el beneficio de la negociación sumando los valores DEAL_PROFIT, DEAL_COMMISSION y DEAL_SWAP y los añade a la variable total_day_profit.
  • Calcula el saldo inicial del día restando el total_day_profit del saldo actual de la cuenta utilizando la función AccountInfoDouble con el parámetro ACCOUNT_BALANCE.

La función devuelve el saldo inicial calculado como un 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…

El código completo de los componentes clave de las funciones del artículo es el siguiente:

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);

Esto es lo que obtenemos.

Ejemplo de lógica de umbral lejano.

UMBRAL LEJANO

Ejemplo de lógica de umbral próximo.

CERCA DEL UMBRAL

Ejemplo del umbral de lógica de éxito:

UMBRAL ALCANZADO

El código completo para crear un limitador de reducción es el siguiente:

//+------------------------------------------------------------------+
//|                                       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); 
}

¡Chin chin por nosotros! Ahora hemos creado un límite de reducción diaria para el Asesor Experto en operaciones de Forex basado en el establecimiento de un límite de retiro diario para las cuentas de operaciones.


Conclusión

Este EA monitorea las actividades comerciales y actualiza el gráfico con etiquetas de texto relevantes que indican el estado del comercio, las ganancias y el permiso para operar en función de ciertos umbrales. El uso de funciones para colocar operaciones y actualizar etiquetas de texto en el gráfico ayuda a organizar el artículo y simplificar el mantenimiento. Hemos analizado los pasos básicos que deben implementarse para automatizar la famosa estrategia de trading de Forex con limitador de reducción diaria en MQL5. Hemos proporcionado la definición y descripción básicas de la estrategia y mostramos cómo se puede crear en MQL5. Los traders pueden utilizar el conocimiento mostrado para desarrollar un sistema limitador de caída diaria más complejo que, más adelante, pueda optimizarse para producir mejores resultados.

Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/15199

Archivos adjuntos |
atesz5870
atesz5870 | 10 jul 2024 en 13:29
¡Bien!
clueboard
clueboard | 11 jul 2024 en 09:40

Parece que acaba de utilizar ForexAlgo-Trader(YouTube) sin darle ningún crédito en absoluto y reclamando su trabajo como el suyo. Desde su video aquí, se puede decir que esta es su línea de código por línea, variable por variable.

Al menos da crédito a quien lo merece. Sí, el código es de código abierto, pero no hay nada malo en dar crédito donde es merecido.

Redes neuronales: así de sencillo (Parte 91): Previsión en el dominio de la frecuencia (FreDF) Redes neuronales: así de sencillo (Parte 91): Previsión en el dominio de la frecuencia (FreDF)
Vamos a continuar con el tema del análisis y la previsión de series temporales en el dominio de la frecuencia. En este artículo, introduciremos un nuevo método de predicción en el dominio de la frecuencia que puede añadirse a muchos de los algoritmos que hemos estudiado anteriormente.
Algoritmo de cerradura de código (Сode Lock Algorithm, CLA) Algoritmo de cerradura de código (Сode Lock Algorithm, CLA)
En este artículo repensaremos las cerraduras de código, transformándolas de mecanismos de protección en herramientas para resolver problemas complejos de optimización. Descubra el mundo de las cerraduras de código, no como simples dispositivos de seguridad, sino como inspiración para un nuevo enfoque de la optimización. Hoy crearemos toda una población de "cerraduras" en la que cada cerradura representará una solución única a un problema. A continuación, desarrollaremos un algoritmo que "forzará" estas cerraduras y hallará soluciones óptimas en ámbitos que van desde el aprendizaje automático hasta el desarrollo de sistemas comerciales.
Vectores y valores propios: Análisis exploratorio de datos en MetaTrader 5 Vectores y valores propios: Análisis exploratorio de datos en MetaTrader 5
En este artículo exploramos diferentes formas en que los vectores propios y los valores propios pueden aplicarse en el análisis exploratorio de datos para revelar relaciones únicas en los datos.
Cómo usar la API de datos JSON en sus proyectos MQL Cómo usar la API de datos JSON en sus proyectos MQL
Imagina que puedes utilizar datos que no se encuentran en MetaTrader, solo obtienes datos de los indicadores mediante análisis de precios y análisis técnico. Ahora imagina que puedes acceder a datos que aumentarán tu poder comercial. Puede multiplicar la potencia del software MetaTrader si combina la salida de otro software, métodos de análisis macro y herramientas ultra avanzadas a través de los datos de la API. En este artículo, le enseñaremos cómo utilizar las API y le presentaremos servicios de datos API útiles y valiosos.