
Creación de un EA limitador de reducción diaria en MQL5
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:
- Explicación del limitador de reducción
- Creación del EA en MQL5
- 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:
- Seguimiento de la reducción de la cuenta
- Alertar al comerciante cuando esté realizando operaciones de alto riesgo
- Seguimiento de la caída diaria del trader
- 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:
- Busca el saldo inicial de la cuenta con la función Acc_B() y lo almacena en la variable 'initialBalance'.
- 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).
- 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.
Ejemplo de lógica de umbral próximo.
Ejemplo del umbral de lógica de éxito:
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
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso
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.