English Русский Deutsch 日本語 Português
preview
Desarrollo de un sistema de repetición (Parte 28): Proyecto Expert Advisor — Clase C_Mouse (I)

Desarrollo de un sistema de repetición (Parte 28): Proyecto Expert Advisor — Clase C_Mouse (I)

MetaTrader 5Probador | 8 febrero 2024, 12:57
324 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior "Desarrollo de un sistema de repetición (Parte 27): Proyecto Expert Advisor (II)", iniciamos el desarrollo de una nueva clase. Sin embargo, al acercarnos al final del artículo, me convencí de la importancia de presentar un enfoque diferente de programación. Esto se presentará solo por curiosidad, con el objetivo de acercarse a un lenguaje más natural.

Para aquellos que han estado programando durante mucho tiempo, lo que mostraré a continuación puede no tener mucho sentido. ¿Por qué molestarse en tratar de acercar la programación a un lenguaje natural? La respuesta es simple: no estás programando para la máquina, sino para otros programadores. Cuando los primeros sistemas capaces de factorizar algo comenzaron a ser producidos, todo dependía del conocimiento avanzado de los ingenieros sobre el proyecto en cuestión. Esa era la realidad en los albores de la computación, cuando ni siquiera existía algún tipo de terminal que permitiera programar algo.

A medida que el desarrollo avanzaba y crecía el interés para que más personas pudieran crear algo, surgían nuevas ideas y métodos para programar esas máquinas, que antes dependían de la modificación de la posición de los conectores. Fue entonces cuando aparecieron los primeros terminales. Con el tiempo, la programación, que inicialmente se realizaba puramente en binario, dejó de ser la norma. Esto sucedió porque los programas crecían muy rápidamente, lo que hacía necesario encontrar una manera más eficiente de leer lo que se estaba programando. Fue entonces cuando surgió el lenguaje ensamblador, la base de todo, transformando el complicado trabajo con binario en algo relativamente más legible, a través de los OpCodes o código mnemotécnico. A medida que pasaba el tiempo y los programas se volvían aún más complejos, exigiendo cada vez más código, comenzaron a aparecer los primeros lenguajes de nivel más alto.

Ya no era necesario tratar directamente con los OpCodes; era posible usar un lenguaje más cercano al natural. Al principio, estos lenguajes se desarrollaron, principalmente, para crear y describir conceptos matemáticos, es decir, servían básicamente para facilitar la traducción de fórmulas en algo que el computador pudiera entender. Este proceso ya no necesitaba ser realizado manualmente por una persona. Así comenzó una nueva era: la de los compiladores, que hacían la traducción del lenguaje humano a algo comprendido por la máquina. Durante años, programé de esta manera, intentando explicar cómo se crean los programas e incentivando a más personas a estudiar y a conseguir transformar sus ideas en algo que el computador pudiera comprender. Sin embargo, me di cuenta de que muchos tienen dificultades iniciales para comprender ciertos conceptos, ya que la programación implica, en gran parte, combinar y utilizar símbolos para expresar lo que se desea crear.

Pero, considerando que el lenguaje MQL5 deriva de los lenguajes C/C++ y tiene la capacidad de hacer cosas de un modo que hace el código más legible, se convierte en ideal para mostrar algo diferente. Entonces, tras analizar un poco y darme cuenta de que puedo ayudar a los entusiastas a comprender lo que se está programando, incluso sin entender completamente el código, decidimos cambiar, por un breve periodo, la forma en que se expresa el código. Al final, todo será comprendido por el compilador, así que para él no hace diferencia. Pero para el entusiasta, hará una gran diferencia, ya que el lenguaje será mucho más cercano al natural. Aunque a primera vista el código pueda parecer extraño y extravagante, la comprensión se volverá considerablemente más fácil para quien está comenzando.

Entonces, te invito a acompañarme en estos breves momentos en que usaremos el lenguaje MQL5 de una manera mucho más cercana al lenguaje natural.


Creamos el archivo Defines.mqh

Dos artículos atrás, abordé el uso de la directiva de compilación #define. Mencioné que hay un caso especial en el que la definición no se elimina al final del archivo, a pesar de no haber mostrado en ese momento una aplicación práctica de este uso de la directiva, por no existir una manera adecuada de hacerlo allí. Dejé, por lo tanto, el tema abierto. El punto crucial es que, de hecho, si entiendes algunas cosas y conceptos sobre MQL5, sabiendo que deriva de C/C++, comenzarás a encontrar más interés en realizar ciertas operaciones en el lenguaje sin mucha dificultad, haciendo el código más legible tanto para quien no es programador y no consigue entender todos aquellos símbolos, como para quien ya es programador y necesita comprender lo que se está haciendo.

Una de esas posibilidades, utilizando la directiva #define, es hacer los códigos más legibles. Es verdad que existen limitaciones, y que a primera vista esto puede parecer bastante extraño. Pero todo se resume a saber definir adecuadamente y sin exageraciones algunas de las simbologías o combinaciones de símbolos presentes en la sintaxis del lenguaje MQL5. De hecho, no estamos creando un nuevo lenguaje, sino haciendo una sustitución de algunas simbologías existentes de manera bastante coherente. Así nace el archivo Defines.mqh, que contendrá tales definiciones para que la sintaxis, antes simbólica, pase a ser algún tipo de palabra o definición más expresiva para un lector humano.

El contenido inicial de este archivo puede verse a continuación, en su totalidad:

//+------------------------------------------------------------------+
#property copyright "Daniel Jose"
/*+------------------------------------------------------------------+
Definitions for increasing the level of the coded language in MQL5, for
more details see the article at the following link:
https://www.mql5.com/en/articles/11349
+------------------------------------------------------------------+*/
#define equal =
#define different !=
#define identical ==
#define and &&
//+------------------------------------------------------------------+

¿Qué hace realmente este modesto trozo de código, que prácticamente no tiene ninguna función? Para comprenderlo, será necesario ponerlo en práctica. Pero, al observar con atención las líneas presentes, cada una de ellas representa una adición que busca hacer el código más legible. Incluso una persona con poca experiencia podrá entender parte, si no la totalidad, de lo que se está programando. La cuestión de la legibilidad del código es algo que siempre debes buscar mejorar. Es preferible tener un código más legible, incluso si eso representa un poco más de trabajo al principio. Pero, al final, valdrá la pena, pues nadie realmente desea lidiar con un código que parezca un jeroglífico, inaccesible incluso para quien lo escribió, y que, muchas veces, ni siquiera el autor logra entender. Y una vez que ese modo de codificación se pierda, ya sea porque la lengua o el individuo dejaron de existir, todo el conocimiento contenido allí también se perderá.

Uno de los métodos para hacer el código más legible es el uso de comentarios. Sin embargo, quien observa los códigos presentes en mis artículos notará que no hago comentarios en ninguno de ellos. Esto es porque, para mí, tales códigos son bastante simples y perfectamente legibles, además de estar documentados de alguna forma. Pero surge la pregunta: sin estos artículos, ¿lograrías comprenderlos? Y esa es exactamente la cuestión aquí. En algún momento, el código puede volverse tan complejo que, sin una buena estructuración, se volvería totalmente ilegible, al punto de que ni siquiera yo lograría mantener y mejorar el código.

Una vez creado el archivo Defines.mqh, necesitamos de alguna manera hacer que el compilador lo utilice. Para ello, incluiremos este archivo en uno de los archivos más básicos de toda la construcción del sistema, el C_Terminal.mqh. Así, al observar la parte referente a las inclusiones del archivo C_Terminal.mqh, verás la siguiente línea de código:

#include "Macros.mqh"
#include "..\..\Defines.mqh"

Presten atención, porque esto es importante. El archivo Macros.mqh se está declarando entre comillas dobles, lo que indica al compilador que este archivo se encuentra en la misma carpeta que el archivo C_Terminal.mqh. Sin embargo, cuando incluimos el archivo Defines.mqh, este también se encuentra entre comillas dobles, pero con una particularidad. Esta diferencia, representada por (..\), instruye al compilador sobre cuántos niveles, partiendo del directorio donde se encuentra el C_Terminal.mqh, debemos subir en la estructura de directorios para encontrar el archivo Defines.mqh. En este caso, necesitamos subir dos niveles, ya que la estructura de directorios contiene diferentes niveles para organizar el código. De esta manera, el archivo Defines.mqh estaría localizado en lo que sería la raíz dentro de la estructura de directorios del proyecto. Si, por cualquier motivo, la raíz del proyecto cambia, esto no afectará al compilador, que siempre buscará el archivo Defines.mqh exactamente en el mismo lugar.

Este tipo de organización es muy interesante, principalmente cuando comienzas a estructurar tu base de encabezados de manera fácil de encontrar y programar algo. Esto permite distribuir fácilmente tu código, o parte de él, sin preocupaciones por la posible falta de un archivo específico. Tener toda la base organizada y preparada para la distribución del código facilita mucho la vida. Ahora que ya explicamos cómo incluir el archivo Defines.mqh en el sistema, podemos empezar a usarlo. Es posible aumentar el número de definiciones para intentar hacer todo más legible. Sin embargo, para mi propósito, las definiciones actuales ya son suficientes. Pero, para que puedas realmente comprender cuánto ayuda esto a hacer el código más legible, facilitando la resolución de problemas que puedan surgir, observa el ejemplo a continuación:

if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
if (m_Info.Study identical eStudyExecute) ExecuteStudy(memPrice);
if_case m_Info.Study identical eStudyExecute then ExecuteStudy(memPrice);

Estas tres líneas quieren decir lo mismo y el compilador entenderá las tres de la misma manera, generando exactamente el mismo código. Sin embargo, en este caso, el archivo Defines.mqh no logrará informar al compilador qué hacer. Sería necesario agregar dos nuevas definiciones, que pueden verse a continuación:

#define if_case if(
#define then )

Al agregar estas dos líneas al archivo Defines.mqh, el compilador será capaz de interpretar correctamente las tres líneas del ejemplo, pero principalmente la línea destacada. Ahora, observen que en la línea destacada tenemos un lenguaje muy similar a un lenguaje natural. Entre las tres líneas mostradas, ella es la más parecida a un lenguaje natural y recibe la denominación de ser la de más alto nivel entre las tres. Es exactamente esto lo que quiero decir cuando hablo de que un código es de alto nivel o de nivel más bajo. Noten que, para el compilador, nada cambiará. Pero para un lector humano, la tercera línea es mucho más sencilla de ser entendida que las otras dos. Este es un caso simple. Pero vamos a observar un caso más complejo, donde tendremos todo el código escrito de la forma mostrada, recordando que hasta los códigos vistos en los artículos anteriores sufrieron este tipo de cambio para hacerlos más legibles. Esto por ahora.

Volvamos al punto en el que nos detuvimos en el artículo anterior, donde íbamos a ver la última función presente en la clase C_Mouse.


Función DispatchMessage: Nuestro medio de comunicación con el mundo exterior

Todas las clases que de alguna manera vengan a recibir eventos de la plataforma MetaTrader 5 tendrán en su portafolio esta función DispatchMessage. Servirá como una manera de que la clase reciba y responda a los eventos generados. El punto más importante que realmente deben entender es que MetaTrader 5 es un programa basado en eventos, que son del tipo REAL TIME. Trabajar con esto es, por decirlo de alguna manera, bastante complicado. Por ello, todo código debe ser muy específico al tratar tales eventos. Pero, antes de observar la función DispatchMessage, necesitamos ver otro código que surgirá en la clase C_Terminal. Este puede verse a continuación:

const double AdjustPrice(const double arg) const { return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits); }

Este código podría haberse colocado en la clase C_Mouse, pero debido a otros factores, decidí colocarlo en la clase C_Terminal. Este código es simplemente una factorización que busca corregir el precio, de modo que siempre sea el valor esperado por el servidor de trading. No es raro ver órdenes siendo rechazadas por el servidor debido al simple hecho de que el precio informado está incorrecto. Muchos terminan desistiendo de estudiar sobre la posibilidad de crear un Expert Advisor simplemente porque, al intentar enviar una orden al servidor de trading, reciben como retorno un error. Algunos tipos de activos tienen una factorización más simple para ajustar el precio, mientras que otros tienen una factorización mucho más compleja que involucra varias cuestiones. Sin embargo, de una manera bastante práctica, la función anterior logra manejar todos estos factores, asegurando que el precio siempre sea el adecuado, independientemente del tipo de activo que se esté utilizando. Esto es de suma importancia para nosotros aquí, pues para un Expert Advisor que se creará y utilizará en un sistema de repetición/simulador, necesitamos que, independientemente del activo, el precio sea realmente adecuado, sea en una cuenta DEMO o incluso en una cuenta REAL. Por lo tanto, usa y abusa de esta función. Si lo haces, podrás ver cómo el sistema realmente logra adaptarse a cualquier tipo de mercado y activo. Entonces, utiliza y explora al máximo este conocimiento.

Ahora que ya hemos visto la función que ajustará el precio según sea necesario, finalmente podemos ver la función DispatchMessage. Ella puede verse en su totalidad justo abajo:

virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      int w equal 0;
      static double memPrice equal 0;
                                
      C_Terminal::DispatchMessage(id, lparam, dparam, sparam);
      switch (id)
      {
         case (CHARTEVENT_CUSTOM + ev_HideMouse):
            ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
            break;
         case (CHARTEVENT_CUSTOM + ev_ShowMouse):
            ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
            break;
         case CHARTEVENT_MOUSE_MOVE:
            ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X equal (int)lparam, m_Info.Data.Position.Y equal (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
            ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price equal AdjustPrice(m_Info.Data.Position.Price));
            if (m_Info.Study different eStudyNull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
            m_Info.Data.ButtonStatus equal (uint) sparam;
            if (CheckClick(eClickMiddle) and ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) different clrNONE)) CreateStudy();
            if (CheckClick(eClickLeft) and (m_Info.Study identical eStudyCreate))
            {
               ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
               ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Info.Data.Position.dt, memPrice equal m_Info.Data.Position.Price);
               m_Info.Study equal eStudyExecute;
            }
            if (m_Info.Study identical eStudyExecute) ExecuteStudy(memPrice);
            break;
         case CHARTEVENT_OBJECT_DELETE:
            if (sparam identical def_NameObjectLineH) CreateLineH();
            break;
      }
   }

Es cierto que gran parte de lo que está sucediendo en el código anterior puede ser comprendido, incluso sin tener mucha experiencia en programación, simplemente observando el código. Pero, ¿por qué sucede esto? ¿Cómo es posible entender un código sin tener un gran conocimiento en programación? La cuestión es que, si comprendes cómo programar en un lenguaje de manera que tu código comience a convertirse en un lenguaje cercano al natural, la comprensión del código se simplifica. De esta forma, incluso personas que no comprenden programación pueden entender lo que se está programando allí. De hecho, gran parte de lo que está sucediendo allí es simple de entender, tan simple que ni siquiera necesitaría ser detallado en una explicación. No obstante, vamos a echar un rápido vistazo a las líneas y empezar a desvelar el código a partir de la declaración de las variables.

int w equal 0;
static double memPrice equal 0;

A pesar de estar declarado de esa forma, debes leer el código de la misma manera como está escrito. Sin embargo, para el compilador, este mismo código será interpretado de la siguiente manera:

int w = 0;
static double memPrice = 0;

Ves que no existe diferencia alguna. Pero con toda seguridad, en ambos casos, cualquier persona podría entender el código. En el primer caso, tenemos el formato literal de la cosa. Pero calma, apenas estamos comenzando, y este ejemplo arriba no refleja completamente lo que podemos hacer para hacer el código más legible.

Vamos a analizar otro fragmento. Este ya no está directamente relacionado con la legibilidad del código, pero aún así necesita ser explicado.

case (CHARTEVENT_CUSTOM + ev_HideMouse):
   ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
   break;

En algunos momentos, necesitamos ocultar la línea de precio en el gráfico. Quienes siguieron el sistema de órdenes mostrado en la secuencia sobre cómo desarrollar un Expert Advisor desde cero vieron que en algunos momentos la línea de precio era ocultada. En aquel código específico, esto se hacía mediante una llamada a un determinado procedimiento que hacía que la línea fuera ocultada. No obstante, ahora vamos a usar un mensaje que será enviado a esta clase C_Mouse, con el fin de ocultar la línea de precio. Esta decisión de usar un mensaje en lugar de un procedimiento se debe al hecho de que deseo construir un sistema más modular y de fácil portabilidad, entre otras cosas. Por eso, tenemos también otro mensaje que sigue el mismo criterio y se muestra a continuación:

case (CHARTEVENT_CUSTOM + ev_ShowMouse):
   ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
   break;

Por ahora, no te preocupes por cómo usar estos mensajes. Eso se explicará en otro momento. Pero de forma simple, puedes entenderlos pensando que en realidad, ambos mensajes vistos arriba serían procedimientos o funciones que deberían ocurrir. Sin embargo, en vez de añadir un procedimiento público o una función exclusivamente para esto, concentraremos todo en un único punto central: la rutina de tratamiento de mensajes. Solo en casos especiales y específicos, utilizaré otro método.

Estos eventos son resultado de una llamada especial que explicaré con más detalle posteriormente, ya que creo que muchos no tienen idea de cómo hacer esto.  Sin embargo, no son eventos disparados por la plataforma MetaTrader 5. Son eventos disparados por nuestro código en momentos muy específicos, con el objetivo de ejecutar alguna acción. Pero también necesitamos tratar eventos provenientes de la plataforma MetaTrader 5. Esto se hace de la siguiente manera:

C_Terminal::DispatchMessage(id, lparam, dparam, sparam);

Esa línea de código, que puede verse en la función DispatchMessage en la clase C_Mouse, reenviará las llamadas a la clase C_Terminal, evitando que necesitemos hacerlo en otro lugar del código. Esto ayuda a evitar olvidos durante la programación y estandariza el código, haciéndolo más rápido de ser programado, analizado, construido y corregido. Sin embargo, no todos los eventos serán tratados en la clase C_Terminal. Algunos serán tratados localmente, es decir, dentro de la clase con la que estamos trabajando. Un ejemplo de esto es el evento visto abajo:

case CHARTEVENT_OBJECT_DELETE:
   if (sparam identical def_NameObjectLineH) CreateLineH();
   break;

Este evento, cuyo código presentado arriba está dirigido al lector y creo que el mismo puede entender lo que se está analizando, es interpretado por el compilador de la siguiente manera:

case CHARTEVENT_OBJECT_DELETE:
   if (sparam == def_NameObjectLineH) CreateLineH();
   break;

Independientemente de cómo se esté presentando el código, el resultado es que cuando un objeto se elimina del gráfico del activo, un mensaje, o mejor dicho, un evento, es disparado por la plataforma. Informa al programa que solicitó tal tipo de mensaje que un objeto ha sido eliminado del gráfico. Al encontrar el manejador CHART_EVENT_OBJECT_DELETE, el programa ejecutará el código presente allí. Si el nombre del objeto informado en la variable constante param es el mismo que estamos probando, el código que se ejecutará recreará la línea de precio.

Ahora tenemos un evento que es el CHART_EVENT_MOUSE_MOVE, que es un poco más extenso. Sin embargo, aunque sea más extenso, no es totalmente complicado. Puedes comprender gran parte del código mostrado a continuación, incluso sin saber programar, simplemente intentando leer literalmente cada una de las líneas. Intenta hacer esto y dime si es más fácil o complicado de entender lo que se está haciendo. No importa si no puedes entender todo el código, la idea es que intentes entender la mayor parte posible sin esforzarte demasiado.

case CHARTEVENT_MOUSE_MOVE:
   ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X equal (int)lparam, m_Info.Data.Position.Y equal (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
   ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price equal AdjustPrice(m_Info.Data.Position.Price));
   if (m_Info.Study different eStudyNull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
   m_Info.Data.ButtonStatus equal (uint) sparam;
   if (CheckClick(eClickMiddle) and ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) different clrNONE)) CreateStudy();
   if (CheckClick(eClickLeft) and (m_Info.Study identical eStudyCreate))
   {
      ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
      ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Info.Data.Position.dt, memPrice equal m_Info.Data.Position.Price);
      m_Info.Study equal eStudyExecute;
   }
   if (m_Info.Study identical eStudyExecute) ExecuteStudy(memPrice);
   break;

Con excepción de los puntos donde hacemos llamadas a procedimientos del lenguaje MQL5, creo que lograste leer todo el código y entendiste algunos puntos con bastante facilidad, como estos que están siendo señalados:

  • Si `m_Info.Study` es diferente de `eStudyNull`, entonces algo deberá ejecutarse.
  • `m_Info.Data.ButtonStatus` es igual a `sparam`.
  • Si se hizo clic con el botón medio y alguna cosa (color de la línea de precio) es diferente de `clrNONE`, entonces ejecuta esto.
  • Si se hizo clic con el botón izquierdo y `m_Info.Study` es igual a `eStudyCreate`, entonces esto se ejecutará.
  • Asigna a `m_Info.Study` el valor `eStudyExecute`.
  • Si `m_Info.Study` es igual a `eStudyExecute`, entonces ejecuta esto.

Ves que incluso leyendo los puntos indicados, como acabo de demostrar, aún así se nota que podemos añadir aún más cosas a nuestro archivo Defines.mqh, con el fin de que el lenguaje sea aún más legible de lo que estoy demostrando. Esto se debe al hecho de que podemos añadir más elementos hasta el punto de hacer un programa extremadamente más legible. Esta es una cualidad de un buen lenguaje de programación y siempre estará presente en programas de buena calidad técnica. Otra forma de hacer el código bastante legible es siempre añadir comentarios a las funciones o puntos más importantes. Y el MQL5 supera mucho al C/C++ en este aspecto. Prueba a poner comentarios en las variables y procedimientos. Al usar el MetaEditor, el mismo mostrará tales comentarios como siendo una pista, lo que ayuda mucho.

¿Y cómo sería de hecho programado el código arriba? O mejor, ¿cómo el compilador de hecho ve el código arriba? Esto se muestra justo abajo:

case CHARTEVENT_MOUSE_MOVE:
   ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X = (int)lparam, m_Info.Data.Position.Y = (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
   ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price));
   if (m_Info.Study != eStudyNull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
   m_Info.Data.ButtonStatus = (uint) sparam;
   if (CheckClick(eClickMiddle) && ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
   if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
   {
      ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
      ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Info.Data.Position.dt, memPrice = m_Info.Data.Position.Price);
      m_Info.Study = eStudyExecute;
   }
   if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
   break;

Ambos códigos realizan exactamente lo mismo. Por lo tanto, explicaré en este qué estamos haciendo para aquellos que no tienen un conocimiento total sobre las funciones presentes en el lenguaje MQL5. La primera cosa que hacemos es convertir las coordenadas gráficas informadas por la plataforma en coordenadas de precio y tiempo. Así, podemos saber dónde está el puntero del ratón en relación al gráfico. A continuación, ajustamos el precio para que sea correcto frente a lo que espera el servidor de trading. De la misma manera, podemos posicionar la línea de precio en el lugar correcto del gráfico. En caso de que estemos haciendo un estudio, necesitamos mover la línea de tiempo de manera correcta. Almacenamos el estado de los botones relacionados con el ratón y verificamos si se produjo un clic del botón medio para hacer un estudio. Sin embargo, el estudio solo se llevará a cabo si la línea de precio está visible en el gráfico. En cuanto se presione el botón izquierdo, el estudio comenzará. Por eso, necesitamos instruir a la plataforma para no mover el gráfico, de manera que podamos arrastrar el ratón con el botón izquierdo presionado sin problemas. Mientras el botón se mantenga presionado, el estudio estará de hecho sucediendo, utilizando todos los objetos que deseamos y queremos poner en el gráfico.

Y antes de terminar el artículo. Vamos a dar un rápido vistazo al código del Expert Advisor, en este actual punto de desarrollo. Este puede ser visto completamente a continuación:

#property copyright "Daniel Jose"
#property description "Generic EA for use on Demo account, replay system/simulator and Real account."
#property description "This system has means of sending orders using the mouse and keyboard combination."
#property description "For more information see the article about the system."
#property version   "1.28"
#property link "https://www.mql5.com/en/articles/11349"
//+------------------------------------------------------------------+
#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
//+------------------------------------------------------------------+
input group "Mouse";
input color     user00 equal clrBlack;          //Price Line
input color     user01 equal clrDarkGreen;      //Positive Study
input color     user02 equal clrMaroon;         //Negative Study
//+------------------------------------------------------------------+
C_Mouse *mouse;
//+------------------------------------------------------------------+
int OnInit()
{
   mouse equal new C_Mouse(user00, user01, user02);
                
   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   delete mouse;
}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   (*mouse).DispatchMessage(id, lparam, dparam, sparam);
   ChartRedraw();
}
//+------------------------------------------------------------------+

Observen que el código es bastante simple. Por eso, no entraré en detalles explicativos en este momento, ya que realmente no es necesario profundizar en explicaciones.


Conclusión

A partir de ahora, podemos observar cómo el sistema está funcionando con el Expert Advisor incorporado. Es verdad que este Expert Advisor cuenta solo con el ratón. No obstante, ahora puedes utilizarlo no solo en el sistema de repetición/simulador, sino también en cuentas DEMO o REAL. Aunque todavía no tenga gran utilidad en esas situaciones, es bastante interesante ver un Expert Advisor que puede ser utilizado en cualquier mercado, activo o situación.

Otro detalle importante es que en el código adjunto, encontrarás las dos últimas clases (C_Terminal y C_Mouse) utilizando el contenido del archivo Defines.mqh. Esto permite hacer el código más legible. Sin embargo, a diferencia de lo que se mencionó al inicio del artículo, donde di a entender que todo el código seguiría esa formatación, esto es solo una curiosidad. Si deseas, puedes de hecho hacer uso de este tipo de técnica. Al inicio de mi carrera como programador C/C++, utilicé este enfoque por un tiempo para comprender mejor la sintaxis del lenguaje. Sé que al principio puede ser bastante confuso, pero con el tiempo te acostumbras. Esta técnica puede ser útil especialmente en proyectos complejos que demandan análisis y combinaciones lógicas y booleanas extensas. La presencia de símbolos dobles en este contexto puede complicarlo para los principiantes. Un ejemplo de esto es el siguiente hecho:

Quién no ha confundido, incluso los programadores experimentados, el uso de una operación de Y LÓGICO (&) con un Y BOOLEANO (&&)? Noten que prácticamente se parecen mucho. Pero el primero realiza la operación Bit a Bit, mientras que el segundo realiza la operación analizando toda la variable y retornando verdadero o falso. Esto atrapa a muchas personas en momentos en que necesitamos crear programas de manera muy rápida.

Por esa razón, no subestimes el conocimiento de ciertas técnicas. Aunque parezcan simples, contribuyen significativamente a hacer el programa más legible y, consecuentemente, más rápido de desarrollar. De una manera interesante, creo haber demostrado cómo realizar las tareas de forma más ágil, asegurando al mismo tiempo que el código esté siempre correcto, sin la necesidad de gastar tiempo intentando entender por qué una parte específica no funciona como debería.


Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/11349

Archivos adjuntos |
Files_-_BOLSA.zip (1358.24 KB)
Files_-_FOREX.zip (3743.96 KB)
Files_-_FUTUROS.zip (11397.51 KB)
Colocando órdenes en MQL5 Colocando órdenes en MQL5
Al crear cualquier sistema comercial, existe una tarea que debemos resolver de forma efectiva. Esta tarea consiste en que el sistema comercial coloque órdenes o las procese de forma automática. El artículo analizará la creación de un sistema comercial desde el punto de vista de la colocación efectiva de órdenes.
Desarrollo de un sistema de repetición (Parte 27): Proyecto Expert Advisor — Clase C_Mouse (I) Desarrollo de un sistema de repetición (Parte 27): Proyecto Expert Advisor — Clase C_Mouse (I)
En este artículo, daremos vida a la clase C_Mouse. Está diseñada para permitir programar al más alto nivel posible. Sin embargo, hablar de programar a niveles altos o bajos no está relacionado con incluir palabrotas o jerga en el código. Todo lo contrario. Cuando mencionamos programación de alto o bajo nivel, nos referimos a lo fácil o difícil que es para otro programador entender el código.
Teoría de categorías en MQL5 (Parte 19): Inducción cuadrática de la naturalidad Teoría de categorías en MQL5 (Parte 19): Inducción cuadrática de la naturalidad
Continuamos analizando las transformaciones naturales considerando la inducción cuadrática de la naturalidad. Pequeñas restricciones en la implementación de las capacidades multidivisa para los asesores ensamblados usando el wizard MQL5 significan que estamos demostrando nuestras capacidades en la clasificación de datos usando un script. Las principales áreas de aplicación son la clasificación de las variaciones de precios y, como consecuencia, su previsión.
Desarrollo de un sistema de repetición (Parte 26): Proyecto Expert Advisor — Clase C_Terminal Desarrollo de un sistema de repetición (Parte 26): Proyecto Expert Advisor — Clase C_Terminal
Podemos comenzar a elaborar un EA para uso en repetición/simulación. Sin embargo, necesitamos algo refinado, no solo una solución cualquiera. No debemos, no obstante, ser intimidados por la complejidad inicial. Es esencial iniciar de algún punto, si no, acabaremos por acomodarnos, reflexionando sobre la dificultad del desafío sin realmente intentar superarlo. La esencia de la programación es exactamente esa: enfrentar un obstáculo y buscar superarlo a través de estudio, pruebas y extensa investigación.