
Desarrollo de un sistema de repetición (Parte 76): Un nuevo Chart Trade (III)
Introducción
En el artículo anterior, "Desarrollo de un sistema de repetición (Parte 75): Un nuevo Chart Trade (II)", en el que expliqué diversos aspectos de la clase C_ChartFloatingRAD. Sin embargo, debido a la densidad del material, la explicación se realizó con el mayor detalle posible cuando fue necesario. Quedó pendiente un procedimiento por analizar. Aunque hubiera incluido este código en el archivo de cabecera C_ChartFloatingRAD.mqh y tratado de explicarlo en el artículo anterior, no habría sido posible hacerlo de forma adecuada. Esto se debe a que, para entender el funcionamiento del procedimiento DispatchMessage, es necesario explicar otro aspecto relacionado.
Pero en este artículo, que comienza ahora, podré explicar con mucho más detalle cómo trabaja realmente el procedimiento DispatchMessage. Este procedimiento es el más importante de la clase C_ChartFloatingRAD, ya que se encarga de generar y responder a los eventos que MetaTrader 5 informará al Chart Trade.
Por lo tanto, para comprender este artículo, es necesario haber leído previamente el artículo anterior. No intentes comprender este artículo sin haber entendido antes a fondo lo que se abordó en el artículo anterior. Los tres últimos artículos, junto con este, explican todo el concepto detrás del indicador Chart Trade. Por lo tanto, es fundamental entender cada uno de estos artículos para comprender lo que se hará en el futuro.
Sin más preámbulos, pasemos a la explicación del procedimiento DispatchMessage, que trataremos en el tema siguiente.
Entendemos cómo funciona DispatchMessage
Si aún no has visto objetos programados aquí en Chart Trade, es probable que aún no hayas comprendido cómo funcionan las cosas en este contexto. Por lo tanto, antes de continuar, te sugiero que revises el artículo anterior. Ahora veremos algo que hace que Chart Trade sea funcional, pero lo haremos únicamente respondiendo y generando eventos. No crearemos ningún objeto. Simplemente convertiremos los eventos informados por MetaTrader 5 en algo operativo.
En el artículo anterior, al examinar el código del archivo de cabecera C_ChartFloatingRAD.mqh, es posible que te dieras cuenta de que hay una región donde falta código. El código que falta en esa misma región se puede observar justo a continuación en el siguiente fragmento destacado.
259. //+------------------------------------------------------------------+ 260. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 261. { 262. #define macro_AdjustMinX(A, B) { \ 263. B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x; \ 264. mx = x - m_Info.Regions[MSG_TITLE_IDE].w; \ 265. A = (B ? (mx > 0 ? mx : 0) : A); \ 266. } 267. #define macro_AdjustMinY(A, B) { \ 268. B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y; \ 269. my = y - m_Info.Regions[MSG_TITLE_IDE].h; \ 270. A = (B ? (my > 0 ? my : 0) : A); \ 271. } 272. 273. static short sx = -1, sy = -1, sz = -1; 274. static eObjectsIDE obj = MSG_NULL; 275. short x, y, mx, my; 276. double dvalue; 277. bool b1, b2, b3, b4; 278. ushort ev = evChartTradeCloseAll; 279. 280. switch (id) 281. { 282. case CHARTEVENT_CHART_CHANGE: 283. x = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_WIDTH_IN_PIXELS); 284. y = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_HEIGHT_IN_PIXELS); 285. macro_AdjustMinX(m_Info.x, b1); 286. macro_AdjustMinY(m_Info.y, b2); 287. macro_AdjustMinX(m_Info.minx, b3); 288. macro_AdjustMinY(m_Info.miny, b4); 289. if (b1 || b2 || b3 || b4) AdjustTemplate(); 290. break; 291. case CHARTEVENT_MOUSE_MOVE: 292. if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) switch (CheckMousePosition(x = (short)lparam, y = (short)dparam)) 293. { 294. case MSG_TITLE_IDE: 295. if (sx < 0) 296. { 297. DeleteObjectEdit(); 298. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 299. sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx); 300. sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny); 301. } 302. if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx); 303. if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my); 304. if (m_Info.IsMaximized) 305. { 306. m_Info.x = (mx > 0 ? mx : m_Info.x); 307. m_Info.y = (my > 0 ? my : m_Info.y); 308. }else 309. { 310. m_Info.minx = (mx > 0 ? mx : m_Info.minx); 311. m_Info.miny = (my > 0 ? my : m_Info.miny); 312. } 313. break; 314. case MSG_BUY_MARKET: 315. ev = evChartTradeBuy; 316. case MSG_SELL_MARKET: 317. ev = (ev != evChartTradeBuy ? evChartTradeSell : ev); 318. case MSG_CLOSE_POSITION: 319. if ((m_Info.IsMaximized) && (sz < 0)) 320. { 321. string szTmp = StringFormat("%d?%s?%c?%d?%.2f?%.2f", ev, _Symbol, (m_Info.IsDayTrade ? 'D' : 'S'), m_Info.Leverage, 322. FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage)); 323. PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp); 324. sz = x; 325. EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp); 326. DeleteObjectEdit(); 327. } 328. break; 329. }else 330. { 331. sz = -1; 332. if (sx > 0) 333. { 334. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 335. sx = sy = -1; 336. } 337. } 338. break; 339. case CHARTEVENT_OBJECT_ENDEDIT: 340. switch (obj) 341. { 342. case MSG_LEVERAGE_VALUE: 343. case MSG_TAKE_VALUE: 344. case MSG_STOP_VALUE: 345. dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT)); 346. if (obj == MSG_TAKE_VALUE) 347. m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue); 348. else if (obj == MSG_STOP_VALUE) 349. m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue); 350. else 351. m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue)); 352. AdjustTemplate(); 353. obj = MSG_NULL; 354. ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable); 355. break; 356. } 357. break; 358. case CHARTEVENT_OBJECT_CLICK: 359. if (sparam == m_Info.szObj_Chart) if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) switch (obj = CheckMousePosition(x = (short)lparam, y = (short)dparam)) 360. { 361. case MSG_DAY_TRADE: 362. m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true); 363. DeleteObjectEdit(); 364. break; 365. case MSG_MAX_MIN: 366. m_Info.IsMaximized = (m_Info.IsMaximized ? false : true); 367. DeleteObjectEdit(); 368. break; 369. case MSG_LEVERAGE_VALUE: 370. CreateObjectEditable(obj, m_Info.Leverage); 371. break; 372. case MSG_TAKE_VALUE: 373. CreateObjectEditable(obj, m_Info.FinanceTake); 374. break; 375. case MSG_STOP_VALUE: 376. CreateObjectEditable(obj, m_Info.FinanceStop); 377. break; 378. } 379. if (obj != MSG_NULL) AdjustTemplate(); 380. break; 381. case CHARTEVENT_OBJECT_DELETE: 382. if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown); 383. break; 384. } 385. ChartRedraw(); 386. } 387. //+------------------------------------------------------------------+ 388. }; 389. //+------------------------------------------------------------------+
Fragmento de código C_ChartFloatingRAD.mqh en falta
Observa que el fragmento anterior encaja a la perfección en la región de código faltante que vimos en el artículo anterior. Por lo tanto, para completar el código de la clase C_ChartFloatingRAD, este fragmento debe colocarse exactamente en esa región y seguir la enumeración de las líneas indicadas. Creo que no te costará nada hacer esto. Ahora veamos cómo funciona.
Lo primero que puedes observar en este fragmento es que no se ha realizado ninguna llamada para crear ningún objeto. Como mencioné, no nos dedicaremos realmente a programar objetos. Chart Trade se crea y se guarda como una plantilla. Esto se realiza completamente dentro de MetaTrader 5. En el artículo anterior, incluí algunas referencias al final para que pudieras comprender el concepto.
Este tipo de programación no ha sido muy utilizado por la mayoría de los programadores, al menos según mi experiencia. Sin embargo, es mucho más práctica y sencilla. Esto se debe a que no es necesario programar cada uno de los objetos ni esforzarse en posicionarlos y ajustarlos. Cuando es necesario, simplemente se hace alguna mención al objeto dentro de la plantilla. Y, en la parte de la programación, solo creas el objeto correcto si no es posible configurarlo directamente en MetaTrader 5. Como expliqué en el artículo anterior, diseñar los elementos y guardarlos como una plantilla es algo bastante raro de realizar. De hecho, el único objeto con el que no logré lidiar de forma adecuada fue el objeto OBJ_EDIT. Esto se debe a que crear toda la lógica para insertar y manipular texto resulta mucho más complicado que simplemente crear el objeto OBJ_EDIT. Por esta razón, es el único objeto que realmente se creará.
Pero volvamos al fragmento. ¿Te das cuenta de que en este fragmento solo estamos gestionando algunos eventos? Si esto es lo que está sucediendo, ¿cómo conseguimos hacer lo que se mostró en el video del artículo anterior? Incluso si no estuviera el video, tienes acceso a los ejecutables en el anexo. Aun así, es posible que te hayas sentido bastante confundido al intentar entender cómo este código, que no contiene múltiples objetos, puede funcionar de esa manera. Es muy probable que hayas pensado que en el procedimiento DispatchMessage aparecerían otros objetos. Sin embargo, si observas el fragmento anterior, verás que no es así. NO EXISTEN OTROS OBJETOS. Todo lo que hacemos es gestionar los eventos que MetaTrader 5 nos proporciona. Entonces, ¿cómo funciona esto?
Para facilitar la comprensión y la explicación, lo haremos por partes. Aquí estamos gestionando cinco tipos de eventos que MetaTrader 5 nos envía. Pero antes de analizar los eventos y la forma en que los estamos gestionando, puedes observar que entre las líneas 273 y 278 declaramos algunas variables. Las variables que se declaran como estáticas son aquellas que podrían estar en el ámbito global de la clase. Sin embargo, como solo se usan en este procedimiento y necesitamos mantener su valor entre llamadas, las definimos como estáticas. Si observas detenidamente, verás que la variable declarada en la línea 278 no es estática. Aun así, se inicializa en el momento de su declaración. Esto nos permite evitar problemas relacionados con el uso incorrecto de esta variable. Observa que el valor que se le asigna corresponde a un evento declarado en el archivo Defines.mqh.
Comencemos analizando el primer evento gestionado, CHARTEVENT_CHART_CHANGE, que se inicia en la línea 282 y se extiende hasta la 290. Este evento se genera cada vez que el gráfico experimenta algún cambio. No se trata de un procedimiento crítico, ya que solo está compuesto por cálculos cuyo objetivo es posicionar y mantener el objeto OBJ_CHART en una ubicación adecuada dentro de la ventana gráfica.
El siguiente evento es CHARTEVENT_MOUSE_MOVE, que comienza en la línea 291 y se extiende hasta la línea 338, siendo el más largo dentro del procedimiento DispatchMessage. Este evento se activa siempre que el mouse se mueve o se presionan los botones del mouse. Sin embargo, de manera predeterminada, MetaTrader 5 no dispara este evento. Es necesario indicarle explícitamente que deseamos recibir eventos del mouse. Esta tarea, es decir, solicitar a MetaTrader 5 que active estos eventos, la realiza el indicador de mouse. Para comprender cómo funciona dicho indicador, consulta los artículos anteriores de esta serie. Es fundamental entender en detalle el funcionamiento del indicador de mouse, ya que este será el encargado de permitir al usuario interactuar con los elementos que estamos creando e implementando.
Pero observa que no estamos gestionando directamente los eventos del mouse. Fíjate en la línea 292, donde lo primero que hacemos es comprobar si se está produciendo un clic con el botón izquierdo. Sin embargo, para que este clic sea válido, solicitamos al indicador de mouse que confirme su validez. ¿Por qué hacemos esto? ¿Por qué no verificamos directamente en el gestor si el clic es válido o no? ¿Por qué molestarnos en preguntar al indicador de mouse?
El motivo de consultar al indicador de mouse sobre la validez del clic es que también se utiliza para generar estudios. Cuando el indicador de mouse está ejecutando un estudio a petición del usuario, ningún clic es válido. No podemos determinar con precisión cuándo ni por cuánto tiempo el indicador de mouse estará en modo estudio. Por esta razón, la forma más efectiva y segura de verificar esto es precisamente preguntarle al indicador de mouse. Si el indicador confirma que el clic es válido, entonces convertimos las posiciones informadas por MetaTrader 5 y verificamos en qué objeto se ha realizado el clic. Esto se realiza mediante la llamada a CheckMousePosition. Podríamos utilizar las coordenadas proporcionadas por el indicador de mouse. Sin embargo, si hiciéramos esto, sería necesario leer el buffer del indicador. Dado que los valores serían los mismos que MetaTrader 5 nos proporciona directamente, no tiene sentido leer el buffer para obtener esta información.
Antes de analizar cómo se gestiona un clic válido, revisemos lo que ocurre en la línea 329, donde se maneja el caso en que el evento CHARTEVENT_MOUSE_MOVE no haya recibido un clic válido o haya sido activado por algún movimiento realizado por el usuario al interactuar con el mouse. En este caso, en la línea 331, se asigna un valor negativo a la variable sz. A continuación, en la línea 332, verificamos si la variable sx tiene un valor positivo. Si esto es así, significa que estamos gestionando un evento del mouse, pero ahora lo dejamos. Por lo tanto, en la línea 334, informamos a MetaTrader 5 de que el mouse puede mover el gráfico. Después, en la línea 335, asignamos un valor negativo a las variables sx y sy, lo que hace que la prueba de la línea 332 falle y evita que se ejecute innecesariamente la línea 334.
Debes tener en cuenta que, después del evento OnTick, que es gestionado en el Expert Advisor, el evento CHARTEVENT_MOUSE_MOVE es uno de los más frecuentes. En algunos casos, este evento CHARTEVENT_MOUSE_MOVE supera con creces al evento OnTick. Si no tomamos las precauciones necesarias, la plataforma MetaTrader 5 se volverá muy lenta y se sobrecargará solo por gestionar estos eventos. Por esta razón, MetaTrader 5 desactiva este evento de forma predeterminada.
Muy bien. Ahora que hemos cubierto esto, volvamos a analizar lo que ocurre entre las líneas 293 y 328, ya que es en esta sección del código donde interactuamos directamente con algunos de los elementos del Chart Trade. Cuando CheckMousePosition devuelve un valor, este puede indicar alguno de los objetos mencionados en esta parte del código, como la barra de título o los botones de compra o venta a mercado o cierre de posiciones. Otros elementos se gestionan en otro lugar y lo explicaré más adelante. Sin embargo, cada uno de estos cuatro elementos requiere un tratamiento diferente. Empecemos por el más complejo: la barra de título.
Cuando el clic ocurre en la barra de título, es posible que el usuario desee mover Chart Trade. Por esta razón, entre las líneas 295 y 313 se encuentra toda la lógica necesaria para que esto suceda. No entraré en detalles ahora, ya que este proceso se explicó en otro artículo de esta misma serie, donde construimos las primeras versiones del Chart Trade. Si tienes dudas, revisa los artículos anteriores, ya que también son importantes para lo que estamos desarrollando aquí. Ten en cuenta que un código no surge de la nada. Se construye e implementa gradualmente hasta adquirir la forma que ves en este momento.
La parte que realmente nos interesa se encuentra entre las líneas 314 y 328, por lo que debes prestar atención al valor definido en la línea 278. Esto es importante porque aquí probaremos dos de las tres condiciones utilizadas. Aunque manejamos tres condiciones, la primera que verificamos es si se produjo un clic válido en el botón de compra a mercado. Si esto ocurre, se asignará el valor a la variable ev para destacar el evento de compra en el mercado. Esto se hace en la línea 315.
Ahora presta atención: En la línea 316, verificamos si el clic se produjo en el botón de venta a mercado. Sin embargo, necesitamos comprobar también si el clic ocurrió en el botón de compra a mercado para asignar correctamente el valor de la variable ev. ¿Por qué hacemos esta comprobación si estamos gestionando un evento diferente? La razón es que, aunque se trata de un evento distinto,
los tres eventos —compra a mercado, venta a mercado y cierre de posición— ejecutan el mismo tipo de acción. No se establece una pausa entre los eventos, sino que se colocan en una cadena. Por esta razón, en la línea 317 debemos comprobar si el evento anterior modificó el valor de la variable ev. Si lo hizo, no debemos realizar esta acción en el siguiente evento de la cadena. De lo contrario, solo se activaría el último evento, ignorando los anteriores.
De todos modos, siempre llegaremos a la línea 318, donde se gestiona el evento para cerrar la posición. En este evento, y solo en este, se realizarán las pruebas necesarias para que MetaTrader 5 active un evento personalizado. Ahora veremos cómo llegamos a este punto, para evitar posibles fallos.
En la línea 319, verificamos si Chart Trade está maximizado o no. Esta prueba es importante porque la función CheckMousePosition no tiene en cuenta si la ventana está maximizada. Presta atención a esto: si no realizamos esta comprobación y haces clic en la posición donde estaría uno de los botones, el evento personalizado se activaría aunque la ventana no estuviera maximizada. Para evitar este problema y garantizar que el evento solo se dispare cuando los objetos estén visibles en el gráfico, verificamos esta condición.
Aquí surge un detalle importante: la ventana debe estar maximizada en el gráfico, es decir, los botones deben ser visibles. Sin embargo, hay un pequeño problema en este proceso de prueba. Es nuestro código, Chart Trade, quien realiza esta comprobación, no MetaTrader 5. ¿Por qué esto es relevante? El problema surge si otro objeto cubre los botones. Si colocas algo que tape los botones y Chart Trade está maximizado, al hacer clic en la región donde se encuentran los botones, será nuestro código —y no MetaTrader 5— quien dispare el evento personalizado. Esta es una falla que planeo corregir en el futuro. No obstante, como no es algo demasiado grave, por ahora podemos convivir con ello. Sin embargo, debes tener este problema en cuenta si utilizas Chart Trade en una cuenta real. NO COLOQUES NADA FRENTE A LOS BOTONES CUANDO Chart Trade ESTÉ MAXIMIZADO.
Existe otra comprobación en esa misma línea, la número 319, cuyo objetivo es verificar y evitar que un clic válido seguido de un movimiento del mouse provoque la activación de múltiples eventos. Se trata de una prueba sencilla en la que el valor de sz debe ser negativo. Si es positivo, significa que ya se ha disparado un evento y que el botón del mouse sigue presionado. Cuando se suelte el botón y se produzca un movimiento del mouse, la línea 331 garantizará que se pueda disparar un nuevo evento personalizado. Aunque es una comprobación simple, evita muchos problemas.
Toda la «magia» ocurre en la línea 321, pero para evitar que sea excesivamente larga, se dividió en dos partes. Por lo tanto, la línea 322 también debe considerarse como parte de la 321. Antes de explicar esta línea, veamos qué hacen las cuatro líneas siguientes. Empezamos por la línea 323, que imprimirá en la caja de herramientas el comando enviado mediante el evento personalizado. Puede parecer innecesario hacer esto, pero créeme, ayuda a evitar muchos problemas. Si estás observando la caja de mensajes y ves un mensaje como el que se imprimirá al enviar un evento personalizado, te alertará para que verifiques qué pudo haber sucedido. Así que nunca ignores los mensajes que se publican en la caja de mensajes de MetaTrader 5, muchos de ellos son extremadamente importantes.
En la línea 324 se realiza un bloqueo para evitar la condición explicada anteriormente, ya que la variable sz se utiliza en la comprobación de la línea 319. El evento personalizado, en cambio, se dispara en la línea 325. Nota que, a diferencia de lo que se hizo en el indicador de control, aquí los datos se envían a través de la región de la cadena. Si esto no fuera posible, tendríamos problemas para enviar grandes cantidades de información entre aplicaciones.
Ahora viene un detalle. Las cadenas en MQL5 siguen el mismo principio que las cadenas en C/C++. Es decir, terminan con un carácter NULL. ¿Por qué lo menciono? Porque es fundamental entender por qué debemos usar la función StringFormat aquí. Si no necesitas imprimir lo que se está enviando mediante el evento personalizado, puedes simplemente tomar el contenido de la línea 321 y colocarlo en la línea 325, reemplazando la variable szTmp con la función StringFormat y utilizando los mismos valores que en la línea 321.
Volviendo al tema de las cadenas de caracteres, el problema radica en que debemos pasar valores numéricos dentro de una cadena. Ahora podrías preguntarte: ¿por qué no usar los campos lparam y dparam del mensaje? ¿Por qué usar el campo sparam? La razón es el tamaño de la información que debemos pasar. Si fueran pocos parámetros, podríamos usar los campos lparam y dparam. Sin embargo, dado que la cantidad de información es considerable, debemos utilizar el campo sparam. Aquí es donde muchos cometen errores, especialmente los principiantes o aspirantes a programadores.
Cuando observas un número, por ejemplo el 65, para una aplicación este valor representará el número 65, pero para otra aplicación puede representar la letra A. Esto podría confundir a algunas personas, pero el problema es que se está interpretando el número 65 de manera literal. Lo correcto sería verlo de forma binaria. En este caso, se representaría en 8 bits como: 0100 0001. Esta es la forma correcta de analizar las cosas cuando trabajamos con programación. Por lo tanto, empecemos a analizar las cosas de manera adecuada. Al observar una cadena (string), no debes pensar en ella como una serie de caracteres imprimibles o comprensibles para los humanos. Debes considerarla como un número enorme, compuesto no por un byte ni por 256 bytes, sino por una cantidad muy grande de bytes.
Desde esta perspectiva, resulta más sencillo entender otra cuestión: ¿cómo indicar que un byte específico corresponde al final de una cadena? Algunos lenguajes de programación utilizan un valor al inicio de la cadena, lo que se conoce como un valor «muerto», que indica la cantidad de caracteres o bytes que contendrá esta. Un ejemplo de esto es el lenguaje Basic. Otro ejemplo sería el antiguo Pascal, donde la longitud de la cadena se almacena al principio de esta.
Por lo tanto, dentro de la cadena en sí, que en estos casos debería considerarse como un array, podemos tener cualquier valor en términos de bytes, es decir, valores entre 0 y 255. Aunque este método es adecuado en muchas situaciones, resulta ineficiente en otras. Esto se debe a que el uso de un array de tamaño fijo puede provocar un uso incorrecto de la memoria. Por ejemplo, podrías necesitar solo 3 bytes, pero si el compilador detecta que, en la misma cadena (o, mejor dicho, array), a veces se requerirán 50 bytes y otras 20 bytes, asignará la mayor área necesaria para el array, es decir, 50 bytes, aunque solo se necesiten 3 bytes en ocasiones.
Debido a esta problemática, otros lenguajes de programación no utilizan esta configuración para las cadenas. Entonces, ¿cómo se sabe cuándo termina una cadena? La solución más común es definir un código o valor que indique el final de la cadena. Generalmente, se utiliza el carácter NULL, que en binario se representa como 0000 0000. La elección de este carácter está relacionada con decisiones tomadas durante la implementación del compilador. Podría haberse elegido cualquier carácter, pero lo importante aquí no es eso, sino la influencia que esto tiene en nuestra transmisión de mensajes mediante cadenas.
Volvamos al caso de los valores numéricos. Nuestro Chart Trade deberá transferir básicamente dos tipos de valores: short y double. El tipo short utiliza 16 bits o 2 bytes. Sin entrar en detalles sobre el tipo double, utilizaremos solo el tipo short para resolver la cuestión. ¿Quién dentro del Chart Trade utiliza un valor short? El nivel de apalancamiento. Bien, ¿cuál es el menor valor que puede tener el apalancamiento? El menor valor es 1. ¿Cómo se representa este valor en binario? Se representa de la siguiente manera: 0000 0000 0000 0001. Aquí lo estoy escribiendo para que se visualice en 16 bits.
Sin embargo, aunque el tipo short utiliza 2 bytes, una cadena (string) está compuesta en realidad por 1 byte por carácter. Por tanto, el primer byte del valor 1 dentro de la cadena representará el carácter NULL. Entonces, ¿la cadena finalizará antes de que se pueda leer información alguna? Sí, por este motivo debemos convertir los valores a caracteres imprimibles. Es decir, da igual el valor binario original, se convierte en una forma comprensible e imprimible, se transmite y luego se reconvierte a su valor binario correspondiente. Por este motivo es necesaria la función StringFormat.
Consideraciones finales
Aunque este artículo parece terminar de forma abrupta, no me siento cómodo explicando en poco tiempo y espacio la segunda parte que necesitamos abordar: el protocolo de comunicación entre las aplicaciones. Aunque puedas pensar que el tema del protocolo es sencillo, en realidad es bastante complicado de explicar rápidamente. Podría limitarme a decirte que la línea 321 crea un protocolo que utilizaremos.
No obstante, hacerlo de esta manera dejaría todo el tema de la comunicación entre programas completamente ambiguo y no te ayudaría en nada si estás intentando entender cómo utilizar este conocimiento en tus propios programas. Aunque MetaTrader 5 lleva ya bastante tiempo en el mercado y cuenta con muchos programadores interesados en desarrollar herramientas para él, no veo a nadie utilizar lo que estoy mostrando aquí. La mayoría de las personas que quieren hacer algo empiezan desde cero o crean programas enormes que, en su mayoría, son extremadamente difíciles de mantener, corregir o mejorar. Sin embargo, gracias al conocimiento que estoy dispuesto a compartir, podrás desarrollar programas más pequeños y mucho más simples, y también mucho más fáciles de corregir y mantener.
Por ello, quiero aprovechar la oportunidad que nos brinda Chart Trade para explicar en detalle cómo funciona el proceso de mensajes. Espero que, como aspirante o estudiante de programación, comprendas que podemos hacer mucho más de lo que la mayoría suele hacer.
Estoy totalmente seguro de que, si te esfuerzas un poco, podrás comprender perfectamente la función de cada uno de los demás eventos (CHARTEVENT_OBJECT_ENDEDIT, CHARTEVENT_OBJECT_CLICK y CHARTEVENT_OBJECT_DELETE). El código asociado a estos eventos es mucho más sencillo que el que hemos analizado en la mayor parte del artículo.
En el próximo artículo, explicaré en detalle por qué la línea 321 tiene ese formato y qué implica para el código del EA que recibe los eventos del Chart Trade. Esto permitirá que el EA ejecute las órdenes generadas por la interacción del usuario con los botones del Chart Trade.
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/12443
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