
Gestión de Riesgo (Parte 2): Implementando el Cálculo de Lotes en una Interfaz Gráfica
- Introducción
- Mejoras en las funciones de obtención de lote y sl
- Explorando las Librerías de Controles y Paneles en MQL5
- Funciones para Crear Componentes del Panel (Labels, Botones, etc.)
- Creación de Objetos en el Área del Cliente
- Funciones de Actualización Dinámica de Combobox y otros Elementos
- Gestión de Eventos de Teclado: Implementación de OnCharEvent
- Inicialización del Panel: Uso del Evento OnInit y Configuración General
- Pruebas y Validación del Panel
- Conclusión
Introducción
Bienvenido, en este artículo, como continuación del anterior, vamos a poner en práctica todo lo que mencionamos y, además, vamos a mejorar algunas funciones del archivo. Para facilitarnos el trabajo, aprovecharemos las poderosas bibliotecas de controles de MQL5. El objetivo es llevar lo que aprendimos a la acción de una manera más eficiente, mostrándote cómo combinar interfaces gráficas con nuestras funciones de gestión de riesgo. Al final, tendrás una herramienta sólida que te permitirá calcular el lotaje y el Stop Loss (SL) de manera precisa y efectiva.
Mejoras en las funciones de obtención de lote y sl
Empezaremos este artículo mejorando las funciones creadas anteriormente, centrándonos en simplificar y optimizar su funcionamiento. Los principales cambios incluyen la adición de mensajes de depuración (PrintFormat y Print), que ayudan a identificar errores en tiempo real, y la creación de nuevas funciones para calcular de manera más eficiente el lote ideal y la distancia del stop loss.
Mejoras en la función GetMaxLote
Esta función calcula el tamaño máximo de lote que puede ser operado con base en el margen libre disponible y las especificaciones del símbolo.
//+----------------------------------------------------------------------------------------------+ //| Calculates the maximum lot size that can be traded based on free margin and symbol specifics | //+----------------------------------------------------------------------------------------------+ double GetMaxLote(ENUM_ORDER_TYPE type, double DEVIATION = 100, double STOP_LIMIT = 50) { double VOLUME = 1.0; // Initial volume size ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(type); double price = PriceByOrderType(_Symbol, type, DEVIATION, STOP_LIMIT); // Price for the given order type double volume_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Volume step for the symbol double margin = EMPTY_VALUE; // Required margin, initialized as empty ResetLastError(); if(!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin)) { Print("OrderCalcMargin() failed. Error ", GetLastError()); return 0; // Exit the function if margin calculation fails } if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0) { PrintFormat("Free margin of %+.2f is invalid, you cannot open trades right now",AccountInfoDouble(ACCOUNT_MARGIN_FREE)); return 0; } double result = MathFloor((AccountInfoDouble(ACCOUNT_MARGIN_FREE) / margin) / volume_step) * volume_step; return result; // Return the calculated maximum lot size }
Mejoras implementadas:
- Mensajes de depuración: Ahora se notifica al usuario cuando el cálculo de margen falla o cuando el margen libre es insuficiente.
- Validación de margen libre: Se incluye una comprobación para evitar cálculos si el margen libre es menor o igual a cero.
Función GetIdealLot
Esta función calcula el lote ideal basándose en el riesgo máximo permitido por operación y las condiciones actuales del mercado.
//+---------------------------------------------------------------------+ //| Determine the optimal lot size based on risk and current conditions | //+---------------------------------------------------------------------+ void GetIdealLot(double& nlot, double glot, double max_risk_per_operation, double& new_risk_per_operation, long StopLoss) { if(StopLoss <= 0) { Print("[ERROR SL] Stop Loss distance is less than or equal to zero, now correct the stoploss distance: ", StopLoss); nlot = 0.0; return; } Print("Max Lot: ", glot, " | RiskPerOperation: ", max_risk_per_operation); new_risk_per_operation = 0; long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double rpo = (glot * (spread + 1 + (StopLoss * tick_value))); if(rpo > max_risk_per_operation) { if(max_risk_per_operation <= 0) return; double new_lot = (max_risk_per_operation / rpo) * glot; new_lot = MathFloor(new_lot / step) * step; new_risk_per_operation = new_lot * (spread + 1 + (StopLoss * tick_value)); nlot = new_lot; } else { new_risk_per_operation = rpo; nlot = glot; } if(nlot <= 0) PrintFormat("The lot %.2f is invalid, the risk %.2f increases or the sl %i decreases",nlot,max_risk_per_operation,StopLoss); }
Mejoras de esta función:
- Validación de Stop Loss: Verifica que la distancia de stop loss sea válida antes de proceder.
- Mensajes claros: Informa si el lote calculado es inválido o si el riesgo debe ajustarse.
Nueva función GetLotByRiskPerOperation
Permite calcular el lote ideal exclusivamente basándose en el riesgo por operación (en USD) y el tipo de orden, eliminando la necesidad de especificar la distancia del stop loss.
//+--------------------------------------------------------------------+ //| Function to obtain the ideal lot based on your risk per operation | //+--------------------------------------------------------------------+ // risk_per_operation in USD, not % double GetLotByRiskPerOperation(double risk_per_operation, const ENUM_ORDER_TYPE order_type, double DEVIATION = 100, double STOP_LIMIT = 50) { double VOLUME = 1.0; // Initial volume size ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(order_type); double price = PriceByOrderType(_Symbol, order_type, DEVIATION, STOP_LIMIT); // Price for the given order type double volume_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Volume step for the symbol double margin = EMPTY_VALUE; // Required margin, initialized as empty ResetLastError(); if(!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin)) { Print("OrderCalcMargin() failed. Error ", GetLastError()); return 0; // Exit the function if margin calculation fails } double result = MathFloor((risk_per_operation / margin) / volume_step) * volume_step; if(result <= 0) { PrintFormat("The lot %.2f is invalid, the risk %.2f increases",result,risk_per_operation); return 0; } PrintFormat("The ideal lot for %.2f risk per trade is %.2f lots",risk_per_operation,result); return result; // Return the calculated maximum lot size } //+------------------------------------------------------------------+
Características clave:
- Simplificación del cálculo: Utiliza únicamente el riesgo en USD y elimina la dependencia de valores adicionales.
- Depuración: Mensajes claros para lotes inválidos o riesgos excesivos.
Mejora en la función CalculateSL
Permite calcular la distancia ideal de stop loss en puntos según el riesgo por operación y el lote elegido.
//+-----------------------------------------------------------------------+ //| Calculate the stop loss distance in points based on risk and lot size | //+-----------------------------------------------------------------------+ long CalculateSL(const ENUM_ORDER_TYPE type, double risk_per_operation, double &chosen_lot, double DEVIATION = 100, double STOP_LIMIT = 50) { double lot = GetLotByRiskPerOperation(risk_per_operation,type,DEVIATION,STOP_LIMIT); chosen_lot = lot; long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double result = ((risk_per_operation / lot) - spread - 1) / tick_value; long ideal_sl = long(MathRound(result)); if(ideal_sl <= 0) { PrintFormat("Stop loss %i invalid, please increase the risk per trade %.2f",ideal_sl,risk_per_operation); return 0; } return ideal_sl; }
Mejoras:
- Integración con GetLotByRiskPerOperation: Utiliza el lote calculado para determinar el stop loss.
- Cálculo mejorado: Asegura que el stop loss siempre sea positivo y válido.
Con estas mejoras, las funciones ahora son más robustas, fáciles de depurar y flexibles. Los mensajes de error permiten identificar rápidamente problemas, y las nuevas implementaciones simplifican el proceso de cálculo tanto del lote como del stop loss.
Explorando las Librerías de Controles y Paneles en MQL5
Antes de empezar a adentrarnos a las librerías de controles de MQL5 debemos saber algo básico sobre como funcionan los objetos en MQL5.
Objetos gráficos en MQL5:
En MQL5, los objetos gráficos son elementos visuales que puedes colocar en un gráfico para representar información o interactuar con el usuario. Estos objetos se posicionan usando dos coordenadas principales:
- X (horizontal): Define la posición del objeto en el eje horizontal.
- Y (vertical): Define la posición en el eje vertical.
Estas coordenadas determinan exactamente dónde aparecerá el objeto en el gráfico o panel.
Propiedades básicas de los objetos gráficos
Además de su posición, los objetos gráficos tienen características como:
- Altura (height): Es la cantidad de espacio que el objeto ocupa verticalmente.
- Anchura (width): Es el espacio que ocupa horizontalmente.
Estas propiedades son esenciales para asegurarte de que el objeto no se salga del área donde debe mostrarse. Por ejemplo, si estás diseñando una interfaz, estas dimensiones garantizan que los elementos se mantengan organizados y dentro de los límites visibles.
Esquina de enlace:
El punto de anclaje es el lugar del objeto que se toma como referencia al usar las coordenadas X e Y. Este punto determina cómo se interpreta la posición del objeto, además existe 4 tipos de puntos de anclaje:
Identificador | Descripción |
---|---|
CORNER_LEFT_UPPER | Centro de coordinadas se encuentra en la esquina superior izquierda del gráfico |
CORNER_LEFT_LOWER | Centro de coordinadas se encuentra en la esquina inferior izquierda del gráfico |
CORNER_RIGHT_LOWER | Centro de coordinadas se encuentra en la esquina inferior derecha del gráfico |
CORNER_RIGHT_UPPER | Centro de coordinadas se encuentra en la esquina superior derecha del gráfico |
Estos 4 tipos de enlace los podemos representar en una imagen tal que así:
Nota: Generalmente y por defecto el tipo de enlanche que más se usa es el CORNER_LEFT_UPPER, esto puede variar dependiendo del uso, o de cuál se te haga más simple, en este artículo solo trabajaremos con el ya mencionado.
Librería de controles de MQL5:
La librería de controles de mql5, es una librería de clases para la creación de controles, paneles, etc. Para acceder a la descripción de los controles podemos revisar la documentación.
Para ello, abre la documentación presionando F1 en tu MetaEditor. Una vez allí, dirígete a la sección de Contenido y haz clic en el libro titulado "Manual de referencia de MQL5". Desplázate hacia el final de la página, donde encontrarás una sección llamada "Biblioteca Estándar". Al hacer clic en ella, verás una lista de diversas librerías para distintas aplicaciones. La que nos interesa es la librería de Controles.
Al seleccionar esta librería, accederás a varias tablas que describen los usos y funcionalidades de cada control, junto con sus respectivas agrupaciones. Para ayudarte a comprender mejor la estructura y las relaciones entre las clases de los controles, he preparado un esquema que muestra cómo funcionan las herencias y las clases asociadas a estos controles.
Como podemos ver, la clase principal es CObject, de la cual se hereda la clase CWnd. A partir de CWnd, se derivan dos clases adicionales: CWndContainer, que está destinada a controles complejos, y CWndObj, que se utiliza para controles más sencillos. En nuestros paneles, utilizaremos ambos tipos de controles según las necesidades específicas de cada caso. No obstante, en este momento nos enfocaremos en el control CAppDialog, que es la clase encargada de crear el panel principal, como el que estamos desarrollando.
Dentro de este panel, comenzaremos a agregar controles básicos, como labels para mostrar textos, comboboxes para seleccionar el tipo de órdenes, botones para guardar las variables y obtener resultados, y edits para que el usuario pueda editar valores, como, por ejemplo, el riesgo por operación que desea establecer.
Diseño de un panel en MQL5: Planificación y elementos gráficos
Antes de empezar a codificar un panel en MQL5, es esencial planificar cómo será su diseño. La mejor manera de hacerlo es crear un boceto visual, como el mostrado en la imagen adjunta, utilizando herramientas simples como paint, canva, etc. Este paso asegura que cada elemento necesario esté incluido y posicionado de forma adecuada, lo que evita omisiones y facilita la implementación, yo lo hice asi:
Analizando la imagen del panel, podemos observar varios controles que cumplen diferentes funciones. Estos controles permiten al usuario interactuar con la interfaz de manera clara y funcional. A continuación, detallo los elementos principales y cómo se usarán en nuestra clase:
Control | Función en nuestro panel | Ejemplo en el panel | Objeto base |
---|---|---|---|
CLabel | Se utiliza para mostrar texto estático en el panel, como descripciones o instrucciones para el usuario. | Etiquetas como "Risk Per Operation %", "Deviation (Points)" y "Stop Limit (Points)". | Este control deriva del objeto gráfico OBJ_LABEL. |
CComboBox | Permite al usuario seleccionar una opción de una lista desplegable. | "Get Lote" y "Get SL", que permiten seleccionar el tipo de orden para el respectivo cálculo del lote y stop loss. | No deriva de un solo objeto, ya que es un objeto compuesto. |
CButton | Son botones interactivos que ejecutan acciones al ser presionados. | El botón junto a "SL Point" que ejecuta una acción, como calcular o confirmar un valor ingresado. También se incluye el botón "Get ideal sl". | Este control deriva del objeto gráfico OBJ_BUTTON. |
CEdit | Permite al usuario ingresar o editar datos manualmente. | Campos como "Risk Per Operation %", "Deviation (Points)", y "Stop Limit (Points)", donde el usuario introduce valores específicos. | Corresponde al objeto gráfico OBJ_EDIT |
Funciones para crear componentes del panel (labels, botones, etc.)
Antes de comenzar a crear las funciones, primero necesitamos crear un Asesor Experto (plantilla) en MetaEditor. Este archivo estará vacío inicialmente, y una vez lo tengas listo, podemos comenzar a incluir las librerías necesarias para trabajar con los componentes de nuestra interfaz gráfica.
Para ello, agrega las siguientes líneas de código al principio de tu archivo:
#include <Controls\Dialog.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> #include <Controls\Button.mqh> #include <Risk Management.mqh> #include <Controls\ComboBox.mqh>
Creación de la clase principal
Comenzaremos definiendo los parámetros que utilizaremos para establecer las dimensiones de varios controles. Estos valores nos permitirán ajustar el tamaño de los componentes de la interfaz, como botones, cuadros de texto y listas desplegables.
//+------------------------------------------------------------------+ //| defines | //+------------------------------------------------------------------+ //--- for edits #define EDIT_HEIGHT (20) // edit height //--- for buttons #define BUTTON_WIDTH (80) // size by X coordinate #define BUTTON_HEIGHT (20) // size by Y coordinate //--- for combo box #define COMBO_BOX_WIDTH (200) // size by X coordinate #define COMBO_BOX_HEIGHT (20) // size by Y coordinate
A continuación, crearemos un array de tipo string con capacidad para 8 elementos. Este array se utilizará para almacenar los diferentes tipos de órdenes en formato de texto.
string elements_order_type[8] = { "ORDER_TYPE_BUY", "ORDER_TYPE_SELL", "ORDER_TYPE_BUY_LIMIT", "ORDER_TYPE_SELL_LIMIT", "ORDER_TYPE_BUY_STOP", "ORDER_TYPE_SELL_STOP", "ORDER_TYPE_BUY_STOP_LIMIT", "ORDER_TYPE_SELL_STOP_LIMIT" };
Creación de la clase para el panel de Gestión de riesgo
Ahora vamos a crear una nueva clase que heredará la parte pública de la clase CAppDialog, la cual es la clase principal para los paneles. Esta clase será la base de nuestro panel de gestión de riesgo.
//+-------------------------------------------------------------------+ //| Class CRiskManagementPanel | //| This class inherits from CAppDialog and will define the panel for | //| managing risk parameters. | //+-------------------------------------------------------------------+ class CRiskManagementPanel : public CAppDialog { // Declare private members here. };
Añadiendo métodos y atributos:
A continuación, empezaremos a añadir métodos y atributos a nuestra clase. Como mencionamos antes, necesitaremos varios controles gráficos, y como se observó en la imagen anterior, estos controles serán agregados a la parte privada de la clase.
private:
CLabel m_label_risk_per_operation;
CEdit m_edit_risk_per_operation;
CLabel m_label_deviaiton;
CEdit m_edit_deviaiton;
CLabel m_label_stop_limit;
CEdit m_edit_stop_limit;
CLabel m_label_get_lote;
CComboBox m_combobox_odertype_get_lot;
CLabel m_label_sl;
CEdit m_edit_sl;
CButton m_buttom_get_lote;
CLabel m_label_result_lote;
CLabel m_label_the_result_lote;
CLabel m_label_get_sl;
CComboBox m_combobox_odertype_get_sl;
CLabel m_label_lote;
CButton m_buttom_get_sl;
CLabel m_label_result_sl;
CLabel m_label_the_result_sl;
Variables privadas para almacenar los datos
Además de los controles, necesitaremos variables privadas que almacenen los datos ingresados por el usuario a través de los controles Edit y ComboBox. Estas variables almacenarán valores como el riesgo por operación, la desviación, el tipo de orden y los valores de stop loss.
// Variables to store the data entered by the user double deviation; // Stores deviation entered by the user double stop_limit; // Stores stop limit entered by the user double risk_per_operation; // Stores risk per operation entered by the user long sl; // Stores stop loss value entered by the user ENUM_ORDER_TYPE order_type_get_sl; // Stores the selected order type for stop loss ENUM_ORDER_TYPE order_type_get_lot; // Stores the selected order type for lot size
Para finalizar la parte privada de nuestra clase, vamos a declarar todas las funciones necesarias para manejar los controles gráficos y la interacción con el usuario. Estas funciones incluirán la creación de objetos, funciones que se ejecutarán cuando el usuario cambie el elemento seleccionado en un ComboBox, y funciones para actualizar los valores de los Labels, entre otras.
Declaración de funciones privadas:
//--- create labels and buttons bool CreateAreaClientPanel(); //--- functions to edit labels dynamically void EditLabelResultSL(string text); void EditLabelResultLote(string text); //--- create controls (buttons, labels, edits, combo boxes) bool CreateEdit(CEdit &m_edit, const string name, const int x1, const int y1, string initial_Text = ""); bool CreateLabel(CLabel &label, const string name, const string text, const int x1, const int y1); bool CreateButton(CButton &button, const string name, const string text, const int x1, const int y1, int x2_ = BUTTON_WIDTH, int y2_ = BUTTON_HEIGHT); bool CreateComboBox(CComboBox &combo_box, const string name, string &elements[], string initial_text, const int x1, const int y1); //--- combo box functions for handling user input void OnChangeComBoxOrderTypeGetLote(); void OnChangeComBoxOrderTypeGetSL();
Declaración de métodos públicos:
En la parte pública de la clase, agregamos dos funciones para la creación del panel y los controles en la zona del cliente. También declaramos un evento OnEvent para manejar los eventos del gráfico y una función para convertir un string en un tipo ENUM_ORDER_TYPE, además de los constructores y destructores.
public: CRiskManagementPanel(void); ~CRiskManagementPanel(void); //--- create panel and controls virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2); //--- chart event handler virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam); //--- function to convert string to ENUM_ORDER_TYPE static ENUM_ORDER_TYPE StringOrderTypeToEnum(const string OrderType);
Con estas declaraciones, tenemos cubierto lo necesario para crear el panel, los controles, y manejar eventos en el gráfico.
Creación de métodos para crear los controles
En este apartado, detallaremos los métodos necesarios para crear controles básicos como labels, botones, edits, y comboBoxes, que formarán parte de nuestro panel de gestión de riesgos. Además, se explicará un método crucial que garantiza el correcto funcionamiento del panel al vincular los controles al área del cliente. Esto asegura que los controles se mantengan alineados con el panel cuando este se mueva o redimensione.
Vinculación de los controles al área del cliente
Para que los controles (como botones, etiquetas, etc.) se muevan junto con el panel al interactuar con él, deben estar vinculados al área del cliente del panel. Para lograr esto, utilizamos el método Add de la clase CDialog. Este método permite registrar los controles en la lista de objetos del área del cliente, asegurando su sincronización con el panel. A continuación, se presenta su definición en CDialog:
//+------------------------------------------------------------------+ //| Add control to the client area (by pointer) | //+------------------------------------------------------------------+ bool CDialog::Add(CWnd *control) { return(m_client_area.Add(control)); } //+------------------------------------------------------------------+ //| Add control to the client area (by reference) | //+------------------------------------------------------------------+ bool CDialog::Add(CWnd &control) { return(m_client_area.Add(control)); }
El método Add tiene dos versiones: una que recibe un puntero al control y otra que utiliza una referencia al mismo. Ambas versiones añaden el control al contenedor m_client_area, que gestiona todos los elementos asociados al panel.
Si omitimos esta vinculación, los controles no seguirán el movimiento del panel, lo que podría generar inconsistencias visuales o problemas de interacción.
Métodos para crear controles básicos
1. Función para crear Labels
Los Labels se utilizan para mostrar texto estático en el panel, como encabezados o instrucciones. La función para crear un Label incluye:
- Definir las coordenadas del control.
- Crear el objeto label mediante el método "create".
- Configurar el texto del label con Text.
- Vincular el label al área del cliente con "Add".
Código de ejemplo:
//+------------------------------------------------------------------+ //| Function to create labels | //+------------------------------------------------------------------+ bool CRiskManagementPanel::CreateLabel(CLabel &label,const string name, const string text, const int x1, const int y1) { //--- coordinates int x2=x1+50; int y2=y1+20; //--- create if(!label.Create(m_chart_id,name+"Label",m_subwin,x1,y1,x2,y2)) return(false); if(!label.Text(text)) return(false); if(!Add(label)) return(false); //--- succeed return(true); }
- label.Create: Crea el control gráfico en las coordenadas dadas.
- label.Text: Establece el texto que se mostrará en el Label.
- Add: Agrega el Label al panel para que sea manejado en conjunto con otros controles.
2. Función para crear botones:
Los botones permiten la interacción directa del usuario con el panel. La función incluye parámetros para el texto, las dimensiones y las coordenadas iniciales. Además, permite ajustar el tamaño de los botones mediante valores predeterminados.
Código de ejemplo:
//+------------------------------------------------------------------+ //| Function to create buttons | //+------------------------------------------------------------------+ bool CRiskManagementPanel::CreateButton(CButton &buttom,const string name, const string text, const int x1, const int y1,int x2_= BUTTON_WIDTH,int y2_ = BUTTON_HEIGHT) { int x2=x1+x2_; int y2=y1+y2_; //--- create if(!buttom.Create(m_chart_id,name,m_subwin,x1,y1,x2,y2)) return(false); if(!buttom.Text(text)) return(false); if(!Add(buttom)) return(false); //--- succeed return(true); }
Dimensiones del Botón:
- x2_ y y2_: Parámetros opcionales que determinan el ancho y alto del botón. Si no se especifican, se usan valores predeterminados (BUTTON_WIDTH y BUTTON_HEIGHT).
Métodos utilizados:
- button.Create: Crea el botón en las coordenadas dadas.
- button.Text: Asigna el texto que aparecerá en el botón.
- Add: Registra el botón en la lista de elementos del panel.
3. Función para crear edits
Los edits permiten al usuario ingresar texto en el panel. Esta función incluye un parámetro opcional para establecer un texto inicial.
Código de ejemplo:
//+------------------------------------------------------------------+ //| Function to create edits | //+------------------------------------------------------------------+ bool CRiskManagementPanel::CreateEdit(CEdit &m_edit, const string name, const int x1, const int y1, string initial_Text = "") { //--- coordinates int y2=y1+EDIT_HEIGHT; int x2 =x1 +100; //--- create if(!m_edit.Create(m_chart_id,name+"Edit",m_subwin,x1,y1,x2,y2)) return(false); //--- allow editing the content if(!m_edit.ReadOnly(false)) return(false); if(!Add(m_edit)) return(false); m_edit.Text(initial_Text); //--- succeed return(true); }
Dimensiones del Campo de Texto:
- El ancho fijo de 100 y la altura definida por EDIT_HEIGHT determinan el tamaño del campo.
Métodos utilizados:
- m_edit.ReadOnly(false): Permite que el campo sea editable por el usuario.
- m_edit.Text: Asigna el texto inicial que se mostrará en el campo.
4. Función para crear ComboBoxes
Los ComboBoxes son controles complejos que permiten al usuario seleccionar un elemento de una lista desplegable. Esta función también incluye la opción de configurar un texto inicial y agregar múltiples elementos.
Código de ejemplo:
//+-------------------------------------------------------------------+ //| Function to create the complex object: combo box | //| This function creates a combo box with multiple selectable items. | //+-------------------------------------------------------------------+ bool CRiskManagementPanel::CreateComboBox(CComboBox &combo_box, const string name, string &elements[], string initial_text, const int x1, const int y1) { //--- calculate coordinates for the combo box int x2 = x1 + COMBO_BOX_WIDTH; int y2 = y1 + COMBO_BOX_HEIGHT; //--- create the combo box control if (!combo_box.Create(m_chart_id, name, m_subwin, x1, y1, x2, y2)) return (false); //--- add items to the combo box for (int i = 0; i < ArraySize(elements); i++) { if (!combo_box.AddItem(elements[i], i)) return (false); } //--- select the initial text combo_box.SelectByText(initial_text); //--- add the combo box to the panel if (!Add(combo_box)) return (false); //--- successfully created the combo box return (true); }
Elementos del ComboBox:
- "elements[]": Un array de strings que contiene los elementos que aparecerán en la lista desplegable.
- "combo_box.AddItem": Agrega cada elemento al ComboBox, asociándolo con un índice único.
Selección Inicial:
- "combo_box.SelectByText(initial_text)": Define cuál será el elemento visible en el ComboBox al crearlo.
Métodos utilizados:
- "combo_box.Create": Inicializa el ComboBox en las coordenadas especificadas.
- "Add": Agrega el ComboBox al panel.
Creación de Objetos en el Área del Cliente
En esta sección, comenzamos a diseñar y crear los elementos del área del cliente en nuestra interfaz de usuario. El objetivo es proporcionar un espacio funcional para que el usuario pueda gestionar y ajustar configuraciones relacionadas con el riesgo y las órdenes de trading. A continuación, describo cómo se divide el área y cómo creamos cada componente.
El panel del cliente se divide en tres secciones principales:
-
Sección General: En esta sección, el usuario podrá editar aspectos generales, como el riesgo por operación, la desviación y el límite de stop.
-
Cálculo del Lote: La segunda parte del panel se dedica a calcular el tamaño del lote basado en el riesgo por operación y el stop loss en puntos. Aquí, se realiza una estimación automática de cuánto riesgo está dispuesto a asumir el usuario.
-
Cálculo del Stop Loss: Finalmente, la parte inferior del panel permite calcular el stop loss en función del porcentaje de pérdida aceptable por operación.
bool CRiskManagementPanel::CreateAreaClientPanel(void) { int x1 = 11; // Initial X coordinate int y1 = 15; // Initial Y coordinate // --- General Section: Risk Per Operation Configuration --- if (!CreateLabel(m_label_risk_per_operation, "L-Risk-Per-operation", "Risk per operation %: ", x1, y1)) return false; // Create the label for risk per operation if (!CreateEdit(m_edit_risk_per_operation, "Risk-Per-operation", x1 + 150, y1)) return false; // Create the editable field for risk per operation y1 += 30; // Move the Y coordinate down for the next section if (!CreateLabel(m_label_deviation, "L-Deviation", "Deviation (Points):", x1, y1)) return false; // Create the label for deviation if (!CreateEdit(m_edit_deviation, "Deviation", x1 + 150, y1, "100")) return false; // Create the editable field for deviation this.deviation = 100; // Default value for deviation y1 += 30; if (!CreateLabel(m_label_stop_limit, "L-StopLimit", "Stop Limit (Points):", x1, y1)) return false; // Create the label for stop limit if (!CreateEdit(m_edit_stop_limit, "Stop Limit", x1 + 150, y1, "50")) return false; // Create the editable field for stop limit this.stop_limit = 50; // Default value for stop limit y1 += 50; // --- Lot Calculation Section --- if (!CreateLabel(m_label_get_lote, "L-Get-Lote-Title", "Get Lote", x1, y1)) return false; // Create the label for lot calculation section if (!CreateComboBox(m_combobox_order_type_get_lot, "ORDER_TYPE_LOT", elements_order_type, "ORDER_TYPE_BUY", x1 + 60, y1)) return false; // Create the combo box to select order type for lot calculation this.order_type_get_lot = ORDER_TYPE_BUY; // Default order type y1 += 30; if (!CreateLabel(m_label_sl, "L-SL", "SL Point: ", x1, y1)) return false; // Create the label for SL point if (!CreateEdit(m_edit_sl, "WRITE-SL", x1 + 60, y1)) return false; // Create the editable field for SL if (!CreateButton(m_button_get_lote, "GET-LOTE", "Save", x1 + 160 + 5, y1)) return false; // Create the button to save the lot calculation y1 += 25; if (!CreateLabel(m_label_result_lote, "L-Result-Lote", "Ideal Lot: ", x1, y1)) return false; // Create the label for displaying the ideal lot if (!CreateLabel(m_label_the_result_lote, "L-The-Result-lot", " ", x1 + 65, y1)) return false; // Create a label to display the calculated ideal lot y1 += 50; // --- Stop Loss Calculation Section --- if (!CreateLabel(m_label_get_sl, "L-Get-SL-Title", "Get SL", x1, y1)) return false; // Create the label for stop loss calculation section if (!CreateComboBox(m_combobox_order_type_get_sl, "ORDER_TYPE_SL", elements_order_type, "ORDER_TYPE_BUY", x1 + 50, y1)) return false; // Create the combo box to select order type for stop loss calculation this.order_type_get_sl = ORDER_TYPE_BUY; // Default order type y1 += 30; if (!CreateLabel(m_label_lote, "L-LOTE", "Get ideal sl:", x1, y1)) return false; // Create the label for getting the ideal stop loss if (!CreateButton(m_button_get_sl, "GET-SL", "Get", x1 + 90, y1)) return false; // Create the button to get the stop loss value y1 += 25; if (!CreateLabel(m_label_result_sl, "L-Result-sl", "Ideal SL:", x1, y1)) return false; // Create the label for displaying the ideal stop loss if (!CreateLabel(m_label_the_result_sl, "L-The-result-sl", " ", x1 + 65, y1)) return false; // Create a label to display the calculated ideal stop loss return true; // If all components are successfully created }
Función principal para crear el panel:
La función Create es el núcleo para inicializar el panel gráfico en la interfaz del usuario. Esta función combina varios elementos clave necesarios para establecer y configurar el panel, asegurándose de que esté correctamente asociado con el gráfico y que las coordenadas y dimensiones sean asignadas de manera adecuada.
Parámetros de entrada
-
chart (long):
- Representa el ID del gráfico en el que se creará el panel.
- Por defecto, en MQL5, el gráfico actual tiene un ID igual a 0.
-
name (string):
- Es el nombre que identifica al panel.
- Este nombre es único y se utiliza para referenciar el panel en otras operaciones.
-
subwin (int):
- Es el número de la subventana del gráfico donde se ubicará el panel.
- Si el panel se coloca en la ventana principal, este valor será 0. Para subventanas adicionales, se asignan valores incrementales (1, 2, etc.).
-
x1 y y1 (int):
- Son las coordenadas iniciales (esquina superior izquierda) del panel en el gráfico.
-
x2 y y2 (int):
- Especifican las dimensiones del panel:
- x2: Determina el ancho.
- y2: Determina el alto.
- Estas dimensiones están dadas en píxeles.
- Especifican las dimensiones del panel:
//+------------------------------------------------------------------+ //| function to create the interface | //+------------------------------------------------------------------+ bool CRiskManagementPanel::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) { if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); if(!CreateAreaClientPanel()) return(false); //--- succeed return(true); }
Funciones de Actualización Dinámica de Combobox y otros Elementos
Ahora siguiendo con la creación de funciones, terminaremos la creación de las funciones dinámicas que modifican los valores de los elementos de la interfaz de usuario, como los ComboBox y las Label que muestran los resultados del cálculo del tamaño del lote (lot size) y del stop loss (SL). Estas funciones permiten editar de manera rápida las etiquetas de los resultados y reaccionar ante los cambios en los ComboBox que el usuario pueda seleccionar.
1. Funciones para la edición de etiquetas
Estas funciones permiten modificar el texto de las etiquetas de los resultados mostrados en la interfaz, específicamente las etiquetas de Tamaño del Lote (lot size) y Stop Loss (SL), en función de los cálculos realizados.
//+------------------------------------------------------------------+ //| Function to edit the text of the lot size label | //+------------------------------------------------------------------+ void CRiskManagementPanel::EditLabelResultLote(string text) { // This function updates the text of the label that shows the ideal lot size. this.m_label_the_result_lote.Text(text); // Set the new text to the label }Esta función actualiza el texto de la etiqueta que muestra el tamaño del lote calculado. Se pasa un valor de texto como parámetro y se actualiza la etiqueta correspondiente con ese valor.
//+------------------------------------------------------------------+ //| Function to edit the text of the stop loss label | //+------------------------------------------------------------------+ void CRiskManagementPanel::EditLabelResultSL(string text) { // This function updates the text of the label that shows the ideal stop loss value. this.m_label_the_result_sl.Text(text); // Set the new text to the stop loss label }
Similar a la función anterior, esta función se encarga de actualizar el texto de la etiqueta que muestra el valor ideal de stop loss. También recibe un texto como parámetro y actualiza la etiqueta correspondiente.
2. Funciones para manejar cambios en los ComboBox
Cada vez que el usuario selecciona un nuevo valor en un ComboBox que maneja el tipo de orden (como compra o venta), se ejecutan estas funciones. Estas funciones actualizan la variable interna que almacena el tipo de orden seleccionado.
//+------------------------------------------------------------------+ //| Function to update the variable that stores | //| the order type to obtain the ideal sl | //+------------------------------------------------------------------+ void CRiskManagementPanel::OnChangeComBoxOrderTypeGetSL(void) { // Iterate through the order types array to find the selected type for(int i = 0; i < ArraySize(elements_order_type); i++) { // If the selected order type matches the one in the array if(m_combobox_order_type_get_sl.Select() == elements_order_type[i]) { // Update the order type variable for stop loss this.order_type_get_sl = StringOrderTypeToEnum(m_combobox_order_type_get_sl.Select()); Print("New order type for sl: ", EnumToString(this.order_type_get_sl)); // Log the selected order type break; } } }
La función se ejecutará cuando el usuario cambia el tipo de orden en el ComboBox para obtener el stop loss. Itera a través de un array de tipos de orden y verifica cuál ha sido seleccionado en el ComboBox. Luego, actualiza la variable interna order_type_get_sl con el tipo de orden correspondiente, utilizando la función StringOrderTypeToEnum.
//+------------------------------------------------------------------+ //| Function to update the variable that stores | //| the order type to obtain the ideal lot | //+------------------------------------------------------------------+ void CRiskManagementPanel::OnChangeComBoxOrderTypeGetLote(void) { // Iterate through the order types array to find the selected type for(int i = 0; i < ArraySize(elements_order_type); i++) { // If the selected order type matches the one in the array if(m_combobox_order_type_get_lot.Select() == elements_order_type[i]) { // Update the order type variable for lot size this.order_type_get_lot = StringOrderTypeToEnum(m_combobox_order_type_get_lot.Select()); Print("New order type for lot: ", EnumToString(this.order_type_get_lot)); // Log the selected order type break; } } }
Similar a la anterior función, esta realiza la misma tarea que la anterior, pero para el tamaño del lote. Cuando el usuario cambia el tipo de orden en el ComboBox para calcular el lote ideal, la función actualiza la variable order_type_get_lot con el tipo de orden seleccionado.
3. Función extra para convertir una cadena (string) a una enumeración ENUM_ORDER_TYPE
Finalmente, esta función convierte una cadena de texto que representa un tipo de orden (por ejemplo, "ORDER_TYPE_BUY") en su correspondiente valor enumerado de tipo ENUM_ORDER_TYPE.
//+------------------------------------------------------------------+ //| Function to convert a string into an order type | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE CRiskManagementPanel::StringOrderTypeToEnum(const string OrderType) { // Convert the string order type to its corresponding enum value if(OrderType == "ORDER_TYPE_BUY") return ORDER_TYPE_BUY; if(OrderType == "ORDER_TYPE_SELL") return ORDER_TYPE_SELL; if(OrderType == "ORDER_TYPE_BUY_LIMIT") return ORDER_TYPE_BUY_LIMIT; if(OrderType == "ORDER_TYPE_SELL_LIMIT") return ORDER_TYPE_SELL_LIMIT; if(OrderType == "ORDER_TYPE_BUY_STOP") return ORDER_TYPE_BUY_STOP; if(OrderType == "ORDER_TYPE_SELL_STOP") return ORDER_TYPE_SELL_STOP; if(OrderType == "ORDER_TYPE_BUY_STOP_LIMIT") return ORDER_TYPE_BUY_STOP_LIMIT; if(OrderType == "ORDER_TYPE_SELL_STOP_LIMIT") return ORDER_TYPE_SELL_STOP_LIMIT; // Return WRONG_VALUE if no match is found return WRONG_VALUE; }
Gestión de eventos de teclado: Implementación de OnCharEvent
Finalizando este artículo definiremos el metodo OnChartEvent ya previamente declarado en nuestra clase, este método se encargará de ejecutar las funciones creadas en la anterior sección además de calcular el lotaje y stop loss cuando se presionan los botones necesarios, la actualización de los edits, etc.
Pero antes de empezar a completar la función mencionada necesitamos saber el funcionamiento de los eventos en la librería de controles de MQL5:
Es importante aclarar que, en lugar de usar los eventos predeterminados como CHARTEVENT_OBJECT_CLICK (que son comunes en muchos entornos de programación), nosotros utilizamos eventos personalizados. Estos eventos están definidos en un archivo específico llamado "Defines.mqh", el cual se encuentra en la carpeta Includes\Controls\Defines.mqh de nuestro proyecto.
El archivo Defines.mqh contiene enumeraciones y definiciones que son esenciales para el funcionamiento de los controles. Además de los valores predeterminados, como los colores de los controles o del panel, lo más relevante para nuestro caso son las definiciones de eventos, que se encuentran al final del archivo. A continuación, te explico cómo están organizados y cómo los utilizamos:
Definición de los Eventos
En el archivo Defines.mqh, se encuentran varios eventos custom (personalizados) que se pueden usar en los controles.
//+------------------------------------------------------------------+ //| Events | //+------------------------------------------------------------------+ #define ON_CLICK (0) // clicking on control event #define ON_DBL_CLICK (1) // double clicking on control event #define ON_SHOW (2) // showing control event #define ON_HIDE (3) // hiding control event #define ON_CHANGE (4) // changing control event #define ON_START_EDIT (5) // start of editing event #define ON_END_EDIT (6) // end of editing event #define ON_SCROLL_INC (7) // increment of scrollbar event #define ON_SCROLL_DEC (8) // decrement of scrollbar event #define ON_MOUSE_FOCUS_SET (9) // the "mouse cursor entered the control" event #define ON_MOUSE_FOCUS_KILL (10) // the "mouse cursor exited the control" event #define ON_DRAG_START (11) // the "control dragging start" event #define ON_DRAG_PROCESS (12) // the "control is being dragged" event #define ON_DRAG_END (13) // the "control dragging end" event #define ON_BRING_TO_TOP (14) // the "mouse events priority increase" event #define ON_APP_CLOSE (100) // "closing the application" event //+------------------------------------------------------------------+
Uso de los Eventos en el Panel
En nuestro caso específico, no necesitamos utilizar todos los eventos disponibles, sino que nos enfocamos en tres eventos clave, que son:
- ON_CLICK: Este evento se dispara cuando se hace clic en un control, como un botón.
- ON_CHANGE: Este evento se utiliza cuando el valor de un control cambia, como en el caso de un ComboBox.
- ON_END_EDIT: Este evento se ejecuta cuando el usuario termina de editar un campo, como un Edit.
Ejemplo de implementación de los eventos en OnChartEvent
Los eventos definidos en Defines.mqh se asignan al parámetro id dentro de la función OnChartEvent. Esta función se encarga de gestionar los eventos que ocurren en el gráfico o panel. El ID se combina con el valor CHARTEVENT_CUSTOM para crear un identificador único para cada evento.
Por ejemplo, el evento ON_CLICK se utilizaría de la siguiente manera:
if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_lote.Id()) { // Acción a ejecutar cuando el botón de obtener lote es presionado // Aquí se llamaría a la función correspondiente para gestionar la acción del botón }
Creación de la función OnEvent
1. Declaración de la función
bool CRiskManagementPanel::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
La función OnEvent es un manejador de eventos que recibe varios parámetros:
- id: El identificador del evento que ha ocurrido.
- lparam: Un parámetro adicional que generalmente contiene información específica de un control o componente.
- dparam: Un parámetro de tipo double, utilizado para pasar valores numéricos.
- sparam: Un parámetro de tipo string, usado para manejar cadenas de texto.
El objetivo de esta función es gestionar los eventos de la interfaz, como cambios en los campos de texto o clics en botones.
2. Verificación de Cambio en el ComboBox de Tipo de Orden
Los ComboBox permiten que el usuario seleccione un valor de una lista desplegable. En este paso, verificamos si el valor de los ComboBox ha sido modificado por el usuario.
Para el ComboBox de tipo de lote:
if(id == ON_CHANGE + CHARTEVENT_CUSTOM && lparam == m_combobox_order_type_get_lot.Id()) { OnChangeComBoxOrderTypeGetLote(); }
Para el ComboBox de tipo de stop loss (SL):
if(id == ON_CHANGE + CHARTEVENT_CUSTOM && lparam == m_combobox_order_type_get_sl.Id()) { OnChangeComBoxOrderTypeGetSL(); }
- ON_CHANGE + CHARTEVENT_CUSTOM: El evento que indica que el valor de un control ha cambiado.
- En la condición "lparam == m_combobox_order_type_get_lot.Id()": Comprobamos que el evento corresponde al ComboBox correcto utilizando su ID.
Dentro de estas condiciones, se ejecutan las funciones OnChangeComBoxOrderTypeGetLote y OnChangeComBoxOrderTypeGetSL, que se encargan de modificar la variable interna ENUM_ORDER_TYPE, que representa el tipo de orden seleccionado.
3. Edits
Los edits permiten al usuario ingresar valores manualmente. Aquí verificamos y actualizamos las variables relacionadas con cada campo de texto editado.
Para el campo de "Risk per operation":
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_risk_per_operation.Id()) { this.risk_per_operation = StringToDouble(m_edit_risk_per_operation.Text()); this.risk_per_operation = NormalizeDouble((this.risk_per_operation / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE), 2); Print("Edit Risk Per Operation: ", this.risk_per_operation); }
Para el campo de "SL":
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_sl.Id()) { this.sl = StringToInteger(m_edit_sl.Text()); Print("Edit SL: ", this.sl); }
Y para otros campos como "Deviation" y "Stop Limit":
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_deviation.Id()) { this.deviation = StringToDouble(m_edit_deviation.Text()); Print("Edit Deviation: ", this.deviation); } if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_stop_limit.Id()) { this.stop_limit = StringToDouble(m_edit_stop_limit.Text()); Print("Edit Stop Limit: ", this.stop_limit); }
- ON_END_EDIT + CHARTEVENT_CUSTOM: Este evento se dispara cuando el usuario termina de editar un campo.
- Cada if verifica si el lparam coincide con el ID del control correspondiente (como un edit.), y si es cierto, la variable interna correspondiente se actualiza con el nuevo valor ingresado por el usuario. Los valores se convierten en sus tipos adecuados (double, int, etc.) y se normalizan cuando es necesario.
4. Obtención del Lotaje y Stop Loss Ideal
Aquí verificamos si el usuario ha hecho clic en los botones para obtener el lote ideal o el stop loss ideal. Dependiendo de qué botón haya sido presionado, se realizan los cálculos correspondientes.
Para el botón de "Get Lot":
if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_lote.Id()) { Print("Risk Per operation: ", this.risk_per_operation); Print("SL in points: ", this.sl); Print("Order type get lot: ", EnumToString(this.order_type_get_lot)); double new_lot; double new_risk_per_operation; GetIdealLot(new_lot, GetMaxLote(this.order_type_get_lot), this.risk_per_operation, new_risk_per_operation, this.sl); PrintFormat("Loss in case the following operation fails, with the parameters: lot %.2f and stop loss of %i points will be %.2f ", new_lot, this.sl, new_risk_per_operation); EditLabelResultLote(DoubleToString(new_lot, 2)); m_button_get_lote.Pressed(false); }
Para el botón de "Get SL":
if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_sl.Id()) { Print("Risk Per operation: ", this.risk_per_operation); Print("Order type get sl: ", EnumToString(this.order_type_get_lot)); double new_lot; long new_sl = CalculateSL(this.order_type_get_sl, this.risk_per_operation, new_lot, this.deviation, this.stop_limit); PrintFormat("For the risk per operation %.2f the chosen lot is %.2f and the ideal stop loss in points is %i", this.risk_per_operation, new_lot, new_sl); EditLabelResultSL(IntegerToString(new_sl)); m_button_get_sl.Pressed(false); }
- ON_CLICK + CHARTEVENT_CUSTOM: Se verifica que el evento de clic corresponde a los botones específicos (m_button_get_lote o m_button_get_sl).
- Se realizan los cálculos utilizando las funciones GetIdealLot y CalculateSL para determinar el lote ideal y el stop loss ideal.
- Los resultados se muestran en las etiquetas del panel (EditLabelResultLote y EditLabelResultSL).
- Finalmente, se desactiva el botón al ponerlo en estado false (m_button_get_lote. Pressed(false)).
5. Retorno del Evento OnChartEvent
Finalmente, después de manejar los eventos, la función devuelve el evento a la clase base CAppDialog para que la función base también procese cualquier otro evento que pueda haber ocurrido:
return(CAppDialog::OnEvent(id, lparam, dparam, sparam));
Este paso es importante para asegurar que no se pierdan otros eventos importantes que no se gestionen específicamente en OnEvent.
Inicialización del Panel: Uso del Evento OnInit y Configuración General
En esta sección, configuraremos el panel de gestión de riesgos utilizando la clase personalizada CRiskManagementPanel. Este panel será responsable de gestionar los cálculos de lotaje, stop-loss y otras funciones esenciales para el trader, presentadas mediante una interfaz gráfica. A continuación, veremos cómo declarar, inicializar y gestionar este panel en el programa.
1. Declaración del Objeto Global
Primero, declaramos el objeto de la clase CRiskManagementPanel en la sección global del programa. Esto permite que el panel esté accesible desde los distintos métodos del código, como OnInit, OnDeinit y OnChartEvent.
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ CRiskManagementPanel risk_panel; // Declare the panel object globally
2. Configuración del Panel en el Evento OnInit
En el evento OnInit, configuramos el panel con el método Create. Este método inicializa la ventana gráfica del panel y define su posición y tamaño. Si la creación del panel falla, el programa retorna el estado INIT_FAILED para indicar un error en la inicialización. Posteriormente, ejecutamos el panel mediante el método run, que pone en marcha la interfaz gráfica para interactuar con el usuario.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Create the risk management panel if(!risk_panel.Create(0, "Test Risk Management", 0, 40, 40, 380, 380)) return(INIT_FAILED); // If panel creation fails, return an error code //--- Run the panel risk_panel.Run(); // Start the graphical interface //--- Return success return(INIT_SUCCEEDED); }
- 0 (chart_id): Indica que el panel se creará en la ventana principal del gráfico.
- "Test Risk Management" (nombre): Define el título que aparecerá en la ventana del panel.
- 0 (subwin): Indica la subventana donde se muestra el panel, como estamos creando el panel en un bot pondremos 0.
- 40, 40 (coordenadas): Especifican la posición inicial del panel en píxeles, coordenadas x(40) e y(40).
- 380, 380 (dimensiones): Definen el ancho y alto del panel en píxeles.
3. Eliminación del panel en el evento OnDeinit
Cuando el programa finaliza o se cierra, debemos liberar los recursos utilizados por el panel. Para esto, utilizamos el método Destroy dentro del evento OnDeinit. Esto asegura que no queden elementos gráficos residuales que puedan interferir con futuros programas.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Destroy the risk management panel risk_panel.Destroy(reason); // Free resources and remove the panel }
4. Manejo de Eventos con OnChartEvent
El método OnChartEvent es clave para capturar e interpretar las interacciones del usuario con el panel, como clics en botones, entradas de datos en campos de texto, o selección de opciones en combos. Estos eventos se procesan mediante el método ChartEvent de la clase CRiskManagementPanel.
//+------------------------------------------------------------------+ //| Expert chart event function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // Event ID const long& lparam, // Event parameter of type long const double& dparam, // Event parameter of type double const string& sparam) // Event parameter of type string { //--- Pass the chart event to the risk panel for processing risk_panel.ChartEvent(id, lparam, dparam, sparam); }
Pruebas y validación del panel
ahora al nombre que le pusiste a tu experto lo ejecutaremos en un símbolo, yo lo estaré ejecutando en el símbolo XAUUSD lo que vendría siendo el oro, el gráfico se vería algo como:
Para que todo sea más transparente y poder ver posibles errores, el balance de mi cuenta es el siguiente
En este momento, como se puede comprobar, no tengo ninguna posición abierta.
Ahora les ensañaré como ustedes pueden usarlo:
1. Se debe rellenar el campo "Risk Per Operation" con un porcentaje (este se le aplicará al balance actual), en la siguiente imagen yo escogeré un 3% de mi cuenta.
Nota: si deseas, puedes modificar los edits de la desviación (deviation) y stop Limit.
2. Escoge qué deseas calcular, para empezar usaré la funcionalidad para obtener el lote ideal para mi operación basándonos en un stop loss en puntos del símbolo, el riesgo por operación y el tipo de orden, yo escogeré un stop loss de 500 puntos y para una orden de compra (ORDER_TYPE_BUY).
Hacemos clic izquierdo en el botón "Save" en la línea de "SL Point" y este es el resultado:
El resultado fue un lote de 0.03 como se puede apreciar en el panel, además en la zona de mensajes salió esto en la parte de expertos:
El mensaje nos da información, como el lote máximo que sería 0.45, el riesgo por operación, además en caso la operación falle usando el lote y stop loss calculado será de 15.45 USD.
3. Ahora probaremos el método para calcular el lote basándonos en el riesgo por operación, hacemos clic izquierdo en el botón "get" (que está en la misma línea que el label "Get ideal sl"):
Como resultado, nos dio un stop loss de 1856 puntos, además de:
Un lote de 0.01 como el ideal para un riesgo del 3.0%.
Conclusión
En este artículo hemos aprendido a implementar una "calculadora" de lotaje y stoploss, una herramienta sumamente útil para cualquier trader. Además, exploramos cómo utilizar las clases y librerías que ofrece MQL5 para crear paneles personalizados. También profundizamos en la estructura de las clases en MQL5, incluyendo la herencia y el uso de objetos como los edits, comboboxes y botones, lo que nos permitió construir interfaces gráficas dinámicas y funcionales.
Este enfoque no solo optimiza el flujo de trabajo, sino que también abre un mundo de posibilidades para desarrollar herramientas personalizadas en MQL5. En la siguiente entrega, comenzaremos a trabajar en la clase principal, consolidando los conceptos presentados en este artículo. Espero que esta guía te haya sido de ayuda y te motive a seguir explorando y creando interfaces gráficas avanzadas para mejorar tu experiencia de trading.
Todos los archivos construidos y utilizados en este artículo se presentan en la siguiente tabla:Nombre del archivo | Descripción |
---|---|
"Risk_Management_Panel.mq5" | Archivo del Asesor Experto que contiene el panel de gestión de riesgo desarrollado en este artículo. |
"Risk_Management.mqh" | Archivo de inclusión que define las funciones para el cálculo del lotaje, stop loss, y otras funciones de gestión de riesgo. |
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