Capítulo 9: Optimización de Código
En este capítulo, exploraremos cómo optimizar el código en MQL5 para mejorar su eficiencia, legibilidad y mantenimiento. Aprenderemos a usar funciones reutilizables, modularizar el código y evitar errores comunes que pueden afectar el rendimiento del programa.
9.1. Uso de Funciones y Procedimientos Reutilizables
El uso de funciones y procedimientos reutilizables es una práctica fundamental para escribir código limpio y eficiente. Las funciones permiten encapsular bloques de código que realizan tareas específicas, lo que facilita la reutilización y reduce la duplicación de código.
Ejemplo Práctico: Creación de Funciones Reutilizables
Supongamos que queremos calcular el tamaño de lote y verificar si el spread está dentro de un rango aceptable. Podemos encapsular estas tareas en funciones separadas:
//+------------------------------------------------------------------+
//| Ejemplo de funciones reutilizables |
//+------------------------------------------------------------------+
#property strict
// Parámetros de entrada
input double RiskPercentage = 1.0; // Porcentaje de riesgo por operación (%)
input int StopLossPips = 50; // Distancia del stop loss en pips
input double MaxSpreadPips = 2.0; // Máximo spread permitido en pips
// Función para calcular el tamaño de lote
double CalculateLotSize(double riskPercentage) {
double balance = AccountInfoDouble(ACCOUNT_BALANCE); // Saldo de la cuenta
double riskAmount = (riskPercentage / 100) * balance; // Monto a arriesgar
double contractSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE); // Tamaño del contrato
// Calcular el tamaño de lote: (monto a arriesgar / valor de punto) / tamaño del contrato
double lotSize = MathFloor((riskAmount / (StopLossPips * Point())) / contractSize);
return lotSize > 0 ? lotSize : 0.01; // Asegurar un mínimo de 0.01 lote
}
// Función para verificar el spread
bool IsSpreadAcceptable() {
double currentSpread = (SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID)) / Point();
if (currentSpread < MaxSpreadPips) {
return true;
} else {
Print("El spread actual (", DoubleToString(currentSpread, 1), " pips) es mayor al máximo permitido.");
return false;
}
}
// Función principal
void OnStart() {
if (IsSpreadAcceptable()) {
double lotSize = CalculateLotSize(RiskPercentage);
Print("Tamaño de lote calculado: ", DoubleToString(lotSize, 2));
} else {
Print("No se puede abrir posición debido al spread elevado.");
}
}
Explicación del código:
- Hemos creado dos funciones:
CalculateLotSize: Calcula el tamaño de lote basado en el riesgo.IsSpreadAcceptable: Verifica si el spread está dentro del rango permitido.
- Estas funciones son reutilizables y pueden ser llamadas desde cualquier parte del código sin necesidad de duplicar lógica.
9.2. Modularización del Código
La modularización consiste en dividir el código en módulos o archivos independientes que realicen tareas específicas. Esto mejora la organización y facilita el mantenimiento.
Ejemplo Práctico: Modularización con Archivos Incluidos
Podemos crear un archivo separado para las funciones relacionadas con la gestión de riesgos y otro para la lógica de trading. Luego, incluimos estos archivos en el script principal.
Archivo RiskManagement.mqh:
//+------------------------------------------------------------------+
//| Gestión de riesgos |
//+------------------------------------------------------------------+
double CalculateLotSize(double riskPercentage) {
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = (riskPercentage / 100) * balance;
double contractSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
double lotSize = MathFloor((riskAmount / (50 * Point())) / contractSize);
return lotSize > 0 ? lotSize : 0.01;
}
Archivo TradingLogic.mqh:
//+------------------------------------------------------------------+
//| Lógica de trading |
//+------------------------------------------------------------------+
bool IsSpreadAcceptable() {
double currentSpread = (SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID)) / Point();
return currentSpread < 2.0;
}
Script Principal:
//+------------------------------------------------------------------+
//| Script principal |
//+------------------------------------------------------------------+
#property strict
#include <RiskManagement.mqh>
#include <TradingLogic.mqh>
input double RiskPercentage = 1.0;
void OnStart() {
if (IsSpreadAcceptable()) {
double lotSize = CalculateLotSize(RiskPercentage);
Print("Tamaño de lote calculado: ", DoubleToString(lotSize, 2));
} else {
Print("No se puede abrir posición debido al spread elevado.");
}
}
Explicación del código:
- Los archivos
.mqhcontienen funciones específicas que pueden ser reutilizados en otros proyectos. - El script principal incluye estos archivos usando
#include, lo que mejora la organización y claridad del código.
9.3. Evitar Errores Comunes
Al desarrollar scripts o Expert Advisors, es importante evitar errores comunes que pueden causar problemas graves, como bucles infinitos o cálculos incorrectos.
Errores Comunes y Soluciones
- Bucles Infinitos:
- Un bucle infinito ocurre cuando una condición nunca se cumple. Para evitarlo, asegúrate de que las condiciones de salida estén bien definidas.
while (true) {
Print("Este bucle nunca terminará.");
}
Solución:
int counter = 0;
while (counter < 10) {
Print("Iteración: ", counter);
counter++;
}
- Cálculos Incorrectos:
- Asegúrate de normalizar precios y volúmenes según las especificaciones del símbolo.
Ejemplo Correcto:
double price = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);
- Comentarios para Facilitar la Comprensión:
- Usa comentarios para explicar qué hace cada bloque de código. Esto es especialmente útil para futuros ajustes o colaboraciones.
Ejemplo de Comentarios:
// Esta función verifica si el spread está dentro del rango permitido
bool IsSpreadAcceptable() {
double currentSpread = (SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID)) / Point();
return currentSpread < 2.0;
}
Interactividad: Ejercicios Prácticos
Ejercicio 1: Crea una función reutilizable que calcule el take profit basado en un múltiplo del stop loss.
Ver solución:Ocultar solución//+------------------------------------------------------------------+
//| Función para calcular el take profit basado en un múltiplo del SL |
//+------------------------------------------------------------------+
#property strict
// Parámetros de entrada
input int StopLossPips = 50; // Distancia del stop loss en pips
input double TakeProfitMultiplier = 2.0; // Múltiplo para calcular el take profit
// Función para calcular el take profit
double CalculateTakeProfit(double entryPrice, int direction) {
// Calcular la distancia del take profit multiplicando el stop loss por el factor
int takeProfitPips = (int)(StopLossPips * TakeProfitMultiplier);
// Calcular el precio del take profit según la dirección de la posición
double takeProfitPrice;
if (direction == OP_BUY) {
takeProfitPrice = NormalizeDouble(entryPrice + takeProfitPips * Point(), _Digits);
} else if (direction == OP_SELL) {
takeProfitPrice = NormalizeDouble(entryPrice - takeProfitPips * Point(), _Digits);
}
return takeProfitPrice;
}
// Función principal para probar la función
void OnStart() {
double entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Precio de entrada (ejemplo: Ask)
int direction = OP_BUY; // Dirección de la posición (compra)
// Calcular el take profit
double takeProfit = CalculateTakeProfit(entryPrice, direction);
// Mostrar el resultado
Print("Precio de entrada: ", DoubleToString(entryPrice, _Digits));
Print("Take Profit calculado: ", DoubleToString(takeProfit, _Digits));
}
Explicación del Código
- Parámetros de Entrada:
StopLossPips: Define la distancia del stop loss en pips.TakeProfitMultiplier: Es el múltiplo que se utilizará para calcular el take profit. Por ejemplo, si el stop loss es de 50 pips y el múltiplo es 2.0, el take profit será de 100 pips.
- Función
CalculateTakeProfit:- Esta función toma como entrada el precio de entrada (
entryPrice) y la dirección de la posición (OP_BUYoOP_SELL). - Calcula la distancia del take profit multiplicando el stop loss (
StopLossPips) por el factor (TakeProfitMultiplier). - Ajusta el precio del take profit según la dirección de la posición:
- Para posiciones largas (
OP_BUY), suma la distancia al precio de entrada. - Para posiciones cortas (
OP_SELL), resta la distancia al precio de entrada.
- Para posiciones largas (
- Usa
NormalizeDoublepara ajustar el precio a la precisión del símbolo.
- Esta función toma como entrada el precio de entrada (
- Prueba de la Función:
- En la función
OnStart, se simula una posición de compra (OP_BUY) con el precio actual de compra (SYMBOL_ASK). - Se llama a la función
CalculateTakeProfitpara calcular el take profit y se muestra el resultado en la ventana de resultados.
- En la función
Resultado Esperado
Cuando ejecutes este script, verás mensajes similares a los siguientes en la ventana de resultados:
Precio de entrada: 1.23456
Take Profit calculado: 1.24456
En este ejemplo:
- El precio de entrada es
1.23456. - El stop loss está configurado en
50 pips. - El múltiplo del take profit es
2.0, lo que significa que el take profit estará a100 pipsdel precio de entrada.
Caso Práctico
Esta función es útil para traders que desean implementar una relación de riesgo/recompensa específica en sus estrategias. Por ejemplo:
- Si deseas una relación de 1:2 (riesgo/recompensa), configura el
TakeProfitMultiplieren2.0. - Si deseas una relación de 1:3, configura el
TakeProfitMultiplieren3.0.
Al encapsular esta lógica en una función reutilizable, puedes aplicarla fácilmente en diferentes partes de tu código sin duplicar la lógica de cálculo.
¡Espero que este ejemplo te ayude a entender cómo crear funciones reutilizables en MQL5!
Ejercicio 2: Modulariza un script existente dividiéndolo en al menos dos archivos .mqh.
A continuación, modularizamos un script existente dividiéndolo en al menos dos archivos .mqh. Esto mejora la organización del código y facilita su reutilización y mantenimiento. El ejemplo se basa en un script que calcula el tamaño de lote y verifica si el spread está dentro de un rango aceptable.
Estructura del Proyecto
- Archivo Principal (
MainScript.mq5): Contiene la lógica principal del script. - Archivo
RiskManagement.mqh: Encapsula funciones relacionadas con la gestión de riesgos (por ejemplo, cálculo del tamaño de lote). - Archivo
TradingLogic.mqh: Encapsula funciones relacionadas con la lógica de trading (por ejemplo, verificación del spread).
Código del Archivo RiskManagement.mqh
Este archivo contiene funciones relacionadas con la gestión de riesgos.
//+------------------------------------------------------------------+
//| RiskManagement.mqh |
//| Gestión de riesgos |
//+------------------------------------------------------------------+
// Función para calcular el tamaño de lote basado en el riesgo
double CalculateLotSize(double riskPercentage) {
double balance = AccountInfoDouble(ACCOUNT_BALANCE); // Saldo de la cuenta
double riskAmount = (riskPercentage / 100) * balance; // Monto a arriesgar
double contractSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE); // Tamaño del contrato
// Calcular el tamaño de lote: (monto a arriesgar / valor de punto) / tamaño del contrato
double lotSize = MathFloor((riskAmount / (50 * Point())) / contractSize);
return lotSize > 0 ? lotSize : 0.01; // Asegurar un mínimo de 0.01 lote
}
Código del Archivo TradingLogic.mqh
Este archivo contiene funciones relacionadas con la lógica de trading.
//+------------------------------------------------------------------+
//| TradingLogic.mqh |
//| Lógica de trading |
//+------------------------------------------------------------------+
// Función para verificar si el spread está dentro del rango permitido
bool IsSpreadAcceptable() {
double currentSpread = (SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID)) / Point();
if (currentSpread < 2.0) {
return true;
} else {
Print("El spread actual (", DoubleToString(currentSpread, 1), " pips) es mayor al máximo permitido.");
return false;
}
}
Código del Archivo Principal (MainScript.mq5)
Este archivo incluye los módulos anteriores y contiene la lógica principal del script.
//+------------------------------------------------------------------+
//| MainScript.mq5 |
//| Script principal |
//+------------------------------------------------------------------+
#property strict
#include <RiskManagement.mqh> // Incluir funciones de gestión de riesgos
#include <TradingLogic.mqh> // Incluir funciones de lógica de trading
// Parámetros de entrada
input double RiskPercentage = 1.0; // Porcentaje de riesgo por operación (%)
// Función principal
void OnStart() {
// Verificar si el spread está dentro del rango permitido
if (IsSpreadAcceptable()) {
// Calcular el tamaño de lote
double lotSize = CalculateLotSize(RiskPercentage);
// Mostrar el resultado
Print("Tamaño de lote calculado: ", DoubleToString(lotSize, 2));
} else {
Print("No se puede abrir posición debido al spread elevado.");
}
}
Explicación del Código
- Modularización:
- Hemos dividido el código en tres archivos:
RiskManagement.mqh: Contiene funciones relacionadas con la gestión de riesgos, como el cálculo del tamaño de lote.TradingLogic.mqh: Contiene funciones relacionadas con la lógica de trading, como la verificación del spread.MainScript.mq5: Es el archivo principal que incluye los módulos anteriores y coordina la ejecución del script.
- Hemos dividido el código en tres archivos:
- Inclusión de Archivos:
- Usamos la directiva
#includepara incluir los archivos.mqhen el script principal. Esto permite reutilizar las funciones definidas en esos archivos sin duplicar código.
- Usamos la directiva
- Reutilización:
- Las funciones en
RiskManagement.mqhyTradingLogic.mqhpueden ser reutilizadas en otros proyectos simplemente incluyendo los archivos correspondientes.
- Las funciones en
- Ventajas de la Modularización:
- Mejora la legibilidad del código al separar las responsabilidades en diferentes archivos.
- Facilita el mantenimiento, ya que cada archivo tiene un propósito específico.
- Permite reutilizar funciones en otros proyectos sin necesidad de copiar y pegar código.
Resultado Esperado
Cuando ejecutes este script, verás mensajes similares a los siguientes en la ventana de resultados:
- Si el spread está dentro del rango permitido:
Tamaño de lote calculado: 0.10
- Si el spread es demasiado alto:
El spread actual (2.5 pips) es mayor al máximo permitido.
No se puede abrir posición debido al spread elevado.
Caso Práctico
La modularización es especialmente útil cuando trabajas en proyectos grandes o colaborativos. Al dividir el código en módulos independientes, puedes:
- Reutilizar funciones en diferentes partes del proyecto.
- Facilitar la colaboración con otros desarrolladores, ya que cada archivo tiene una responsabilidad clara.
- Reducir errores al mantener el código organizado y bien estructurado.
¡Espero que este ejemplo te ayude a entender cómo modularizar tu código en MQL5!
Ejercicio 3: Identifica y corrige un error potencial en el siguiente código:mql
for (int i = 0; i <= PositionsTotal(); i++) {
uint ticket = PositionGetTicket(i);
Print("Ticket: ", ticket);
}
//+------------------------------------------------------------------+
//| Script para listar tickets de posiciones abiertas |
//+------------------------------------------------------------------+
void OnStart() {
// Obtener el número total de posiciones abiertas
int totalPositions = PositionsTotal();
// Iterar sobre todas las posiciones abiertas
for (int i = 0; i < totalPositions; i++) { // Corrección: Usar "i < totalPositions" en lugar de "i <= totalPositions"
uint ticket = PositionGetTicket(i); // Obtener el ticket de la posición en el índice "i"
// Verificar si el ticket es válido
if (ticket > 0) {
Print("Ticket de la posición ", i, ": ", ticket);
} else {
Print("Error al obtener el ticket de la posición ", i, ". Código de error: ", GetLastError());
}
}
}
Explicación del Código
- Identificación del Error:
- El error original estaba en la condición del bucle
for:i <= PositionsTotal(). PositionsTotal()devuelve el número total de posiciones abiertas, pero los índices de las posiciones comienzan desde0hastaPositionsTotal() - 1.- Al usar
i <= PositionsTotal(), el bucle intenta acceder a una posición inexistente (PositionsTotal()), lo que puede causar errores o comportamientos inesperados.
- El error original estaba en la condición del bucle
- Corrección del Bucle:
- Cambiamos la condición del bucle a
i < totalPositions, asegurando que solo se itere sobre los índices válidos de las posiciones abiertas.
- Cambiamos la condición del bucle a
- Validación del Ticket:
- Añadimos una verificación para asegurarnos de que el ticket obtenido sea válido (
ticket > 0). - Si el ticket no es válido, mostramos un mensaje de error junto con el código de error correspondiente usando
GetLastError().
- Añadimos una verificación para asegurarnos de que el ticket obtenido sea válido (
- Mejoras Adicionales:
- Guardamos el resultado de
PositionsTotal()en una variable (totalPositions) para evitar llamar repetidamente a la función dentro del bucle. - Mostramos mensajes claros en la ventana de resultados para facilitar la depuración.
- Guardamos el resultado de
Resultado Esperado
Cuando ejecutes este script corregido, verás mensajes similares a los siguientes en la ventana de resultados:
- Si hay posiciones abiertas:
Ticket de la posición 0: 123456
Ticket de la posición 1: 789012
- Si no hay posiciones abiertas:
No hay posiciones abiertas para listar.
- Si ocurre un error al obtener el ticket:
Error al obtener el ticket de la posición 2. Código de error: 4756
Caso Práctico
Este tipo de error es común cuando se trabaja con arrays o colecciones indexadas en MQL5. Es importante recordar que los índices siempre comienzan desde 0 y terminan en n-1, donde n es el tamaño total de la colección. Al corregir este error, evitamos problemas como:
- Acceso a memoria no asignada.
- Errores de ejecución que pueden interrumpir el funcionamiento del EA o script.
- Resultados incorrectos debido a datos inválidos.
¡Espero que este ejemplo te ayude a identificar y corregir errores similares en tus proyectos futuros!
Conclusión: Optimizar el código en MQL5 no solo implica mejorar su rendimiento, sino también hacerlo más legible y fácil de mantener. Usar funciones reutilizables, modularizar el código y evitar errores comunes son prácticas clave para lograrlo.
En el próximo capítulo, exploraremos técnicas avanzadas de depuración y pruebas para garantizar que tu código funcione correctamente en diferentes escenarios.
¡Sigue practicando y perfeccionando tus habilidades en MQL5!
