![Múltiples indicadores en un gráfico (Parte 05): Convirtamos el MetaTrader 5 en un sistema RAD (I)](https://c.mql5.com/2/49/Multiple-indicators-on-one-chart-9Part-05a-Turning-MetaTrader-5-into-a-RAD-system-yI6_600x314.jpg)
Múltiples indicadores en un gráfico (Parte 05): Convirtamos el MetaTrader 5 en un sistema RAD (I)
Introducción
A pesar de no saber programar, muchas personas son bastante creativas y tienen grandes ideas, pero la falta de conocimientos o de entendimiento sobre la programación les impide hacer algunas cosas. ¿Qué tal si creamos nuestra propia interfaz Chart Trade, para enviar órdenes de mercado, o para ajustar los parámetros que se utilizarán en las órdenes pendientes? ¿Y esto sin programar nada, sólo usando las funciones que estarán dentro del EA? Estamos curiosos, pues veamos cómo se vería esto en nuestros monitores:
Quizá pensemos: "Pero, ¿cómo podríamos hacer eso? No sabemos nada de programación, o lo que sabemos no será suficiente para permitirnos hacer esto". Bueno, el Chart Trade que se ve en la imagen de arriba se logró dentro de la propia plataforma MT5 y se diseñó como se muestra en la figura a continuación:
Ahora que hemos visto lo que este artículo nos mostrará, deberíamos estar entusiasmados y llenos de ideas para crear nuestro propio gráfico, pero tenemos que seguir unos cuantos pasos para que todo funcione y una vez que el código de soporte esté configurado, nuestra creatividad será el único límite para el diseño de nuestro propio IDE Chart Trade. Este artículo es una continuación de los anteriores, por lo que para una comprensión total y completa, recomiendo ver los artículos anteriores a este de esta misma serie.
Dicho esto, manos a la obra.
Planificación
Para empezar el gráfico que usará como IDE, debe tener sus propiedades modificadas, esto para reducir posibles efectos secundarios, no es que vayan a ocurrir, pero al dejar el gráfico limpio será más fácil construir y diseñar la interfaz Chart Trade, así que abrimos las propiedades del gráfico y dejamos las cosas como las imágenes de abajo.
De esta forma, la pantalla quedará completamente limpia y libre de cualquier cosa que pueda estorbar durante el diseño de nuestro IDE. Ahora entendamos una cosa. Nuestro IDE se guardará como un archivo de configuración, es decir, un TEMPLATE, por lo que podremos usar cualquiera de los objetos que nos proporciona MT5, pero por cuestiones prácticas solo usaremos algunos de ellos, ver todos los disponibles en Tipos de objetos en MT5.
Objeto | Tipo de coordenadas utilizadas para el posicionamiento | Interesante para IDE |
---|---|---|
Texto | Fecha y Precio | NO |
Etiqueta | Posición X e Y | SÍ |
Botón | Posición X e Y | SÍ |
Gráfico | Posición X e Y | SÍ |
Bitmap | Fecha y Precio | NO |
Etiqueta Bitmap | Posición X e Y | SÍ |
Editar | Posición X e Y | SÍ |
Evento | Solo se usa la fecha | NO |
Etiqueta rectangular | Posición X e Y | SÍ |
Como vamos a utilizar un sistema que puede estar en cualquier región de la pantalla, no es práctico utilizar un objeto que no utilice el sistema de coordenadas X e Y para posicionar las cosas, ya que si lo hacemos podemos tener un IDE diferente al que creamos, así que nos limitamos a 6 objetos, los cuales son más que suficientes para crear una interfaz.
La idea es colocar los objetos en un orden lógico, de la misma manera que se dibuja algo en la pantalla, primero se crea un fondo y luego se colocan las cosas una encima de otra, situando y ajustando los objetos a medida que se diseña la interfaz. Vea cómo sucederá esto:
Veamos que es algo muy sencillo, solo requerirá un poco de práctica, y dominaremos esta forma de diseñar nuestro propio IDE, la idea aquí se parece mucho a la utilizada en los programas RAD utilizados para crear interfaces de programas donde la interfaz de usuario puede ser muy compleja al diseñarse vía código, no es que no podamos crear una interfaz directamente a través de código, pero utilizando esta forma es mucho más rápida y fácilmente modificable, lo cual es ideal para aquellos que quieren una interfaz con su propio estilo.
Cuando hayamos terminado podemos tener una interfaz como la que se muestra a continuación, o incluso más cool, pero aquí he tratado de utilizar todos los objetos posibles para que se puedan probar, aunque nada impide crear las cosas de la manera que más nos guste.
Esta es la primera fase de la creación de nuestro IDE, ahora necesitamos crear un código que realmente soporte esta interfaz y la haga funcional, si bien el simple hecho de que podamos elaborar nuestra propia interfaz personalizada, ya es un motivo para sentirnos aún más motivados y esta motivación se traducirá en código.
El siguiente paso es guardar esta interfaz como un archivo de configuración, ya podemos guardarlo y, utilizando el código de la versión anterior, podremos mostrarlo como si fuera un puntero, esto implica que no necesitaremos hacer mayores cambios en el código original, pero si vamos a verificar la posibilidad de recibir eventos, o enviar eventos a nuestro IDE, veremos que esto no es posible. Pero si la interfaz fue creada usando los propios objetos de MT5, ¿por qué no puedo enviar y recibir eventos de esos objetos? La respuesta no es muy sencilla de dar, pero sí de mostrar. Añadiendo el siguiente código a la versión original de EA, podemos comprobarlo.
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { switch (id) { case CHARTEVENT_OBJECT_CLICK: Print(sparam); break; // .... Código restante ... } }
Este código nos informará el nombre del objeto que está recibiendo un clic y generando un evento, que en este caso es el evento CHARTEVENT_OBJECT_CLICK, sin embargo el mensaje impreso será el nombre del objeto creado por el EA y no el nombre de los objetos en el IDE. Esto puede parecer a primera vista un gran problema, que imposibilita el uso de nuestro IDE, pero hay una solución muy sencilla: Leer el archivo de configuración y luego crear los objetos como se indica en este archivo, esto creará nuestro IDE directamente en el gráfico. Luego, analizando el archivo de configuración ( TPL ) encontramos los datos que necesitamos utilizar.
Llave | Descripción |
---|---|
<chart> | Inicia el archivo de configuración |
</chart> | Finalizar el archivo de configuración |
<window> | Inicia la estructura de elementos presentes en el gráfico. |
</window> | Finaliza la estructura de los elementos presentes en el gráfico. |
<indicator> | Inicia la estructura que informa datos referentes a algún indicador |
</indicator> | Finaliza la estructura que informa los datos del indicador |
<object> | Inicia la estructura que informa datos sobre algún objeto. |
</object> | Finaliza la estructura que informa datos sobre el objeto. |
Esta estructura se ve así dentro del archivo TPL.
<chart> .... DATOS <window> ... DATOS <indicator> ... DATOS </indicator> <object> ... DATOS </object> </window> </chart>
la parte que verdaderamente nos interesa se encuentra entre <object> y </object>, y puede haber varias de estas estructuras, cada una indicando un objeto único. Así que para empezar tenemos que cambiar la ubicación del archivo, tenemos que ponerlo en un lugar que podamos leerlo, y este lugar es el directorio FILES, podemos modificar el lugar que he utilizado, de todas formas el archivo debe estar dentro del árbol FILES. Un detalle importante, a pesar de que el sistema ha recibido una modificación que permite limpiar el gráfico cuando se utiliza el archivo de configuración del IDE, lo ideal es que también se tenga un archivo limpio con el mismo nombre en el directorio Profiles\Templates, esto minimiza al máximo algunos residuos presentes en la plantilla por defecto como se vio en los artículos anteriores. Los principales cambios se pueden ver a continuación resaltados:
#include <Auxiliar\Chart IDE\C_Chart_IDE.mqh> //+------------------------------------------------------------------+ class C_TemplateChart : public C_Chart_IDE { .... Otras partes del código .... //+------------------------------------------------------------------+ void AddTemplate(const eTypeChart type, const string szTemplate, int scale, int iSize) { if (m_Counter >= def_MaxTemplates) return; if (type == SYMBOL) SymbolSelect(szTemplate, true); SetBase(szTemplate, (type == INDICATOR ? _Symbol : szTemplate), scale, iSize); if (!ChartApplyTemplate(m_handle, szTemplate + ".tpl")) if (type == SYMBOL) ChartApplyTemplate(m_handle, "Default.tpl"); if (szTemplate == "IDE") C_Chart_IDE::Create(m_IdSubWin); ChartRedraw(m_handle); } //+------------------------------------------------------------------+ void Resize(void) { #define macro_SetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_Info[c0].szObjName, A, B) int x0 = 0, x1, y = (int)(ChartGetInteger(Terminal.Get_ID(), CHART_HEIGHT_IN_PIXELS, m_IdSubWin)); x1 = (int)((ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS, m_IdSubWin) - m_Aggregate) / (m_Counter > 0 ? (m_CPre == m_Counter ? m_Counter : (m_Counter - m_CPre)) : 1)); for (char c0 = 0; c0 < m_Counter; x0 += (m_Info[c0].width > 0 ? m_Info[c0].width : x1), c0++) { macro_SetInteger(OBJPROP_XDISTANCE, x0); macro_SetInteger(OBJPROP_XSIZE, (m_Info[c0].width > 0 ? m_Info[c0].width : x1)); macro_SetInteger(OBJPROP_YSIZE, y); if (m_Info[c0].szTemplate == "IDE") C_Chart_IDE::Resize(x0); } ChartRedraw(); #undef macro_SetInteger } //+------------------------------------------------------------------+ ... Código restante }
Observemos que estamos añadiendo la interfaz del IDE como una nueva clase y ésta es heredada por nuestra clase original, lo que significa que la clase original tendrá su funcionalidad aumentada, y esto sin causar ningún efecto secundario en el código original.
Hasta aquí era la parte fácil, pero ahora tenemos que hacer algo un poco más complicado, lo cual servirá de soporte a nuestro IDE. Primero tenemos que crear el protocolo de mensajes que utilizará el sistema, este protocolo permitirá que el sistema funcione como se puede ver a continuación:
Observemos que podemos modificar los datos del sistema, lo cual no es posible por el momento, pero añadiendo un protocolo de mensajes será posible hacer que nuestro IDE sea funcional, así que definamos algunas cosas:
Mensaje | Intención |
---|---|
MSG_BUY_MARKET | Envía una orden de mercado BUY |
MSG_SELL_MARKET | Envía una orden de mercado SELL |
MSG_LEVERAGE_VALUE | Datos sobre el factor de apalancamiento |
MSG_TAKE_VALUE | Datos sobre el valor de la ganancia financiera |
MSG_STOP_VALUE | Datos sobre el valor de stop financiera |
MSG_RESULT | Datos sobre el resultado actual de la posición abierta |
MSG_DAY_TRADE | Informa si la operación se cerrará al final del día o no |
Este protocolo es un paso muy importante, y una vez definido tendremos que hacer un cambio en el archivo de configuración, por lo que al abrir la lista de objetos, tendremos que cambiar cosas para que quede como se muestra a continuación:
La interfaz que estoy mostrando tendrá la lista de objetos como la imagen, prestemos atención al hecho de que el NOMBRE de los objetos corresponde a cada uno de los mensajes que vamos a utilizar. el nombre que tendrán los demás objetos es irrelevante, ya que se utilizan para ayudar en el modelado del IDE, si bien que los objetos con los nombres de los mensajes recibirán y enviarán mensajes. En caso de que se quiera utilizar más mensajes o de otro tipo, basta con hacer los cambios necesarios en el código de la clase y el propia MT5 proporcionará los medios para que los mensajes se intercambien entre el IDE y el código del EA.
Pero aún necesitamos estudiar el archivo TPL, para saber cómo crear nuestra clase objeto, y ahora trataremos de saber cómo se declaran los objetos dentro del archivo TPL. Es cierto que tendremos menos acceso a las propiedades de los objetos dentro del archivo TPL que a través de la programación, ya que la propia interfaz del MT5 nos da menos acceso a las propiedades de los objetos, pero aun así el acceso que tendremos será suficiente para que nuestro IDE sea funcional.
Bien, dentro del archivo TPL, vemos la estructura de nuestro interés, <object> hasta </object>, pero al analizar los datos dentro de esta estructura no nos parece claro cómo saber a qué tipo de objeto se hace referencia, pero... mirando con calma, entendemos que el tipo de objeto está definido por la variable type, ésta recibe un valor diferente para cada uno de los objetos, después de un tiempo tendremos la siguiente tabla, basada en los objetos que queremos utilizar:
Valor de la variable TYPE | Objeto referenciado |
---|---|
102 | OBJ_LABEL |
103 | OBJ_BUTTON |
106 | OBJ_BITMAP_LABEL |
107 | OBJ_EDIT |
110 | OBJ_RECTANGLE_LABEL |
Bien, nuestra clase ya puede empezar a tomar forma, por lo que la primera rutina que será elaborada se puede ver a continuación:
bool Create(int nSub) { m_CountObject = 0; if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false; FileReadInteger(m_fp, SHORT_VALUE); for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = ""; m_SubWindow = nSub; m_szLine = ""; while (m_szLine != "</chart>") { if (!FileReadLine()) return false; if (m_szLine == "<object>") { if (!FileReadLine()) return false; if (m_szLine == "type") { if (m_szValue == "102") if (!LoopCreating(OBJ_LABEL)) return false; if (m_szValue == "103") if (!LoopCreating(OBJ_BUTTON)) return false; if (m_szValue == "106") if (!LoopCreating(OBJ_BITMAP_LABEL)) return false; if (m_szValue == "107") if (!LoopCreating(OBJ_EDIT)) return false; if (m_szValue == "110") if (!LoopCreating(OBJ_RECTANGLE_LABEL)) return false; } } } FileClose(m_fp); return true; }
Notemos que lo primero que hay que hacer es abrir el archivo en modo lectura y como archivo binario, y hacemos esto para no dejar escapar nada, pues cuando usamos un editor HEXA tenemos la siguiente vista del archivo TPL. Vean que empieza con un valor muy curioso.
¿Suena confuso? Pues no lo es, el archivo sigue el sistema UTF-16 para codificar las cosas, y como sabemos que la organización se hace por líneas, vamos a crear una rutina para leer una línea entera de una vez, y para ello creamos el siguiente código:
bool FileReadLine(void) { int utf_16 = 0; bool b0 = false; m_szLine = m_szValue = ""; for (int c0 = 0; c0 < 500; c0++) { utf_16 = FileReadInteger(m_fp, SHORT_VALUE); if (utf_16 == 0x000D) { FileReadInteger(m_fp, SHORT_VALUE); return true; } else if (utf_16 == 0x003D) b0 = true; else if (b0) m_szValue = StringFormat("%s%c", m_szValue, (char)utf_16); else m_szLine = StringFormat("%s%c", m_szLine, (char)utf_16); if (FileIsEnding(m_fp)) break; } return (utf_16 == 0x003E); }
La lectura intenta ser lo más eficiente posible, por lo que cuando encontramos un signo igual ( = ), hacemos la separación ya durante la lectura para evitar una separación posterior. El bucle limita la string a un máximo de 500 caracteres, pero esto fue arbitrario, y puede ser cambiado según sea necesario. A cada nueva línea encontrada, la función regresará, dándonos el contenido de la misma para que podamos proceder al análisis adecuado.
Necesitamos algunas variables para soportar nuestro protocolo de mensajes, y estas se pueden ver a continuación:
class C_Chart_IDE { protected: enum eObjectsIDE {eRESULT, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP}; //+------------------------------------------------------------------+ #define def_HeaderMSG "IDE_" #define def_MaxObject eEDIT_STOP + 32 //+------------------------------------------------------------------+ private : int m_fp, m_SubWindow, m_CountObject; string m_szLine, m_szValue; bool m_IsDayTrade; struct st0 { string szName; int iPosX; }m_ArrObject[def_MaxObject]; // ... Resto do código da classe ....
la definición def_MaxObject indica el número máximo de objetos que podemos mantener, este número se consigue en base al número de mensajes, más un número extra de objetos que vamos a utilizar, en este caso tendremos el máximo de 40 objetos, pero se puede aumentar si es necesario. Los primeros 8 objetos serán utilizados para transportar mensajes entre el IDE y la MT5, y el alias de estos mensajes se ve en la enumeración eObjectsIDE, es importante tenerlo en cuenta por si se quiere ampliar el sistema o adaptarlo a otra cosa.
Esta es sólo la primera parte del sistema de apoyo, tenemos otro punto a observar, y esta parte es una constante, que tiene que ver exactamente con el sistema de mensajes de hecho, la forma en que el lenguaje MQL5 trata con las constantes es un poco embarazoso para aquellos que ya programan en C / C ++, esto se debe a que en estos lenguajes, una constante se declara en la propia declaración de la variable, pero aquí la forma de hacerlo puede hacer que el código se complique un poco más, pero es algo con lo que podemos vivir ya que el uso de este tipo de constantes es poco frecuente, la forma de hacerlo se puede ver a continuación resaltada.
public : static const string szMsgIDE[]; // ... Restante do código da classe .... }; //+------------------------------------------------------------------+ static const string C_Chart_IDE::szMsgIDE[] = { "MSG_RESULT", "MSG_BUY_MARKET", "MSG_SELL_MARKET", "MSG_DAY_TRADE", "MSG_CLOSE_POSITION", "MSG_LEVERAGE_VALUE", "MSG_TAKE_VALUE", "MSG_STOP_VALUE" }; //+------------------------------------------------------------------+
Observemos las constantes definidas, pues son exactamente los mismos valores utilizados en los nombres de los objetos en la interfaz, el sistema fue diseñado para no distinguir entre mayúsculas y minúsculas, tratando a todos de la misma manera, pero este comportamiento puede ser cambiado, aunque aconsejo no hacerlo.
Una vez realizados estos pasos, podemos pasar al siguiente, así que volvamos al archivo TPL y analicemos algunas cosas, echemos un vistazo a un fragmento del archivo a continuación:
Luego de saber que tipo de objeto usar, tenemos una serie de datos que informan las propiedades del objeto, como nombre, posición, color, fuente, en fin... tenemos que llevar estas mismas propiedades a nuestros objetos internos, ya que la cosa es muy repetitiva, podemos crear una rutina genérica, que se puede ver a continuación:
bool LoopCreating(ENUM_OBJECT type) { #define macro_SetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B) #define macro_SetString(A, B) ObjectSetString(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B) int c0; bool b0; string sz0 = m_szValue; while (m_szLine != "</object>") if (!FileReadLine()) return false; else { if (m_szLine == "name") { b0 = false; StringToUpper(m_szValue); for(c0 = eRESULT; (c0 <= eEDIT_STOP) && (!(b0 = (m_szValue == szMsgIDE[c0]))); c0++); c0 = (b0 ? c0 : m_CountObject); m_ArrObject[c0].szName = StringFormat("%s%04s>%s", def_HeaderMSG, sz0, m_szValue); ObjectDelete(Terminal.Get_ID(), m_ArrObject[c0].szName); ObjectCreate(Terminal.Get_ID(), m_ArrObject[c0].szName, type, m_SubWindow, 0, 0); } if (m_szLine == "pos_x" ) m_ArrObject[c0].iPosX = (int) StringToInteger(m_szValue); if (m_szLine == "pos_y" ) macro_SetInteger(OBJPROP_YDISTANCE , StringToInteger(m_szValue)); if (m_szLine == "size_x" ) macro_SetInteger(OBJPROP_XSIZE , StringToInteger(m_szValue)); if (m_szLine == "size_y" ) macro_SetInteger(OBJPROP_YSIZE , StringToInteger(m_szValue)); if (m_szLine == "offset_x" ) macro_SetInteger(OBJPROP_XOFFSET , StringToInteger(m_szValue)); if (m_szLine == "offset_y" ) macro_SetInteger(OBJPROP_YOFFSET , StringToInteger(m_szValue)); if (m_szLine == "bgcolor" ) macro_SetInteger(OBJPROP_BGCOLOR , StringToInteger(m_szValue)); if (m_szLine == "color" ) macro_SetInteger(OBJPROP_COLOR , StringToInteger(m_szValue)); if (m_szLine == "bmpfile_on" ) ObjectSetString(Terminal.Get_ID() , m_ArrObject[c0].szName, OBJPROP_BMPFILE, 0, m_szValue); if (m_szLine == "bmpfile_off" ) ObjectSetString(Terminal.Get_ID() , m_ArrObject[c0].szName, OBJPROP_BMPFILE, 1, m_szValue); if (m_szLine == "fontsz" ) macro_SetInteger(OBJPROP_FONTSIZE , StringToInteger(m_szValue)); if (m_szLine == "fontnm" ) macro_SetString(OBJPROP_FONT , m_szValue); if (m_szLine == "descr" ) macro_SetString(OBJPROP_TEXT , m_szValue); if (m_szLine == "readonly" ) macro_SetInteger(OBJPROP_READONLY , StringToInteger(m_szValue) == 1); if (m_szLine == "state" ) macro_SetInteger(OBJPROP_STATE , StringToInteger(m_szValue) == 1); if (m_szLine == "border_type" ) macro_SetInteger(OBJPROP_BORDER_TYPE , StringToInteger(m_szValue)); } m_CountObject += (b0 ? 0 : (m_CountObject < def_MaxObject ? 1 : 0)); return true; #undef macro_SetString #undef macro_SetInteger }
Cada objeto recibirá un nombre y se almacenará en el lugar apropiado, pero la línea resaltada muestra algo diferente. Cuando creamos el IDE, éste debe comenzar en la esquina superior izquierda del gráfico, pero esta posición X no será necesariamente la esquina superior izquierda de la subventana, esta posición debe corresponder a la esquina superior izquierda del objeto OBJ_CHART al que estará vinculado el IDE, y este objeto se indica al cargar el template del IDE, por lo que puede estar en cualquier lugar dentro de la subventana, si no corregimos esto el IDE no se mostrará en el lugar correcto, por lo que almacenamos el valor X y lo utilizamos posteriormente para mostrar el objeto en el lugar correcto, la rutina que hace la visualización correcta del IDE se puede ver a continuación.
La información principal utilizada en los objetos, ya está definida, pero si es necesario añadir alguna otra información, basta con incluirla en el conjunto de comandos y modificar la propiedad con el valor correspondiente.
void Resize(int x) { for (int c0 = 0; c0 < m_CountObject; c0++) ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, x + m_ArrObject[c0].iPosX); };
Antes de ver como se manejan los mensajes, tenemos que ver otras dos rutinas igual de importantes, ya que el sistema puede estar recibiendo valores del EA, que se adquieren durante la inicialización, y estos valores tienen que estar correctamente presentados y ajustados de forma que si se va a utilizar el Chart Trade se puedan configurar las órdenes directamente en él, ya sea para enviar una orden de mercado o una orden pendiente, sin necesidad de utilizar el EA como ocurría antes, ambas rutinas se ven a continuación:
void UpdateInfos(bool bSwap = false) { int nContract, FinanceTake, FinanceStop; nContract = (int) StringToInteger(ObjectGetString(Terminal.Get_ID(), m_ArrObject[eEDIT_LEVERAGE].szName, OBJPROP_TEXT)); FinanceTake = (int) StringToInteger(ObjectGetString(Terminal.Get_ID(), m_ArrObject[eEDIT_TAKE].szName, OBJPROP_TEXT)); FinanceStop = (int) StringToInteger(ObjectGetString(Terminal.Get_ID(), m_ArrObject[eEDIT_STOP].szName, OBJPROP_TEXT)); m_IsDayTrade = (bSwap ? (m_IsDayTrade ? false : true) : m_IsDayTrade); ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eCHECK_DAYTRADE].szName, OBJPROP_STATE, m_IsDayTrade); NanoEA.Initilize(nContract, FinanceTake, FinanceStop, clrNONE, clrNONE, clrNONE, m_IsDayTrade); } //+------------------------------------------------------------------+ void InitilizeChartTrade(int nContracts, int FinanceTake, int FinanceStop, color cp, color ct, color cs, bool b1) { NanoEA.Initilize(nContracts, FinanceTake, FinanceStop, cp, ct, cs, b1); if (m_CountObject < eEDIT_STOP) return; ObjectSetString(Terminal.Get_ID(), m_ArrObject[eEDIT_LEVERAGE].szName, OBJPROP_TEXT, IntegerToString(nContracts)); ObjectSetString(Terminal.Get_ID(), m_ArrObject[eEDIT_TAKE].szName, OBJPROP_TEXT, IntegerToString(FinanceTake)); ObjectSetString(Terminal.Get_ID(), m_ArrObject[eEDIT_STOP].szName, OBJPROP_TEXT, IntegerToString(FinanceStop)); ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eCHECK_DAYTRADE].szName, OBJPROP_STATE, m_IsDayTrade = b1); }
Observemos que el IDE está vinculado al sistema de órdenes, por lo que los cambios realizados en el sistema se reflejarán en el sistema de órdenes, de esta forma no necesitaremos ir cambiando los datos en el EA, como se hacía antes, ahora podemos hacerlo directamente en el IDE, o en nuestro Chart Trade, y esto se hace a través de estas 2 rutinas anteriores aliadas al sistema de mensajes que veremos ahora.
void DispatchMessage(int iMsg, string szArg, double dValue = 0.0) { if (m_CountObject < eEDIT_STOP) return; switch (iMsg) { case CHARTEVENT_CHART_CHANGE: if (szArg == szMsgIDE[eRESULT]) { ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_BGCOLOR, (dValue < 0 ? clrLightCoral : clrLightGreen)); ObjectSetString(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_TEXT, DoubleToString(dValue, 2)); } break; case CHARTEVENT_OBJECT_CLICK: if (StringSubstr(szArg, 0, StringLen(def_HeaderMSG)) != def_HeaderMSG) return; szArg = StringSubstr(szArg, 9, StringLen(szArg)); StringToUpper(szArg); if ((szArg == szMsgIDE[eBTN_SELL]) || (szArg == szMsgIDE[eBTN_BUY])) NanoEA.OrderMarket(szArg == szMsgIDE[eBTN_BUY]); if (szArg == szMsgIDE[eBTN_CANCEL]) { NanoEA.ClosePosition(); ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false); } if (szArg == szMsgIDE[eCHECK_DAYTRADE]) UpdateInfos(true); break; case CHARTEVENT_OBJECT_ENDEDIT: UpdateInfos(); break; } }
Y surge la pregunta: ¡¿Pero eso es todo?! Sí, este es el sistema de mensajería que permite a MT5 comunicarse con el IDE, es algo muy simple debo confesar, pero sin esta rutina el IDE no funcionaría y el sistema no podría ser construido. Puede parecer un poco complicada la forma de hacer funcionar esto en el EA, pero no lo es, gracias a la programación POO, el código del EA seguirá siendo súper sencillo, lo único un poco más complicado es la actualización del valor del resultado que aparecerá en el IDE, esto debería hacerlo la rutina OnTick pero para facilitarlo hice uso de los datos proporcionados por MT5 por lo que la rutina quedó así, esta parte es la más crítica, ya que esta rutina es la más solicitada de todas, por lo que tiene que ser igualmente la más rápida.
void OnTick() { SubWin.DispatchMessage(CHARTEVENT_CHART_CHANGE, C_Chart_IDE::szMsgIDE[C_Chart_IDE::eRESULT], NanoEA.CheckPosition()); }
es decir, a cada nueva cotización, se envía un mensaje a la clase y se actualiza el valor resultante en la operación, recordemos que esta rutina tiene que estar bien optimizada, de lo contrario podemos tener serios problemas.
Conclusión
A veces parece imposible hacer algunas cosas, pero me gustan los retos, y éste de mostrar cómo hacer un sistema RAD dentro de una plataforma que no fue desarrollada originalmente para esto, fue bastante interesante. Espero que este sistema que comenzó con algo sencillo y sin pretensiones pueda motivarnos a intentar llegar donde pocos llegan.
Pronto aportaré novedades a este EA, pero por ahora, ¡diviértanse...!
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/10277
![Consejos de un programador profesional (Parte III): Registro Conexión al sistema de recopilación y análisis de logs Seq](https://c.mql5.com/2/45/tipstricks.png)
![Tutorial de DirectX (Parte I): Dibujamos el primer triángulo](https://c.mql5.com/2/45/ramka.png)
![Múltiples indicadores en un gráfico (Parte 06): Convirtamos el MetaTrader 5 en un sistema RAD (II)](https://c.mql5.com/2/45/variety_of_indicators__4.png)
![Gráficos en la biblioteca DoEasy (Parte 97): Procesamiento independiente del desplazamiento de los objetos de formulario](https://c.mql5.com/2/45/MQL5-avatar-doeasy-library3-2__1.png)
![MQL5 - Lenguaje de estrategias comerciales para el terminal de cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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