English Русский 中文 Deutsch 日本語 Português
preview
Del básico al intermedio: Variables (III)

Del básico al intermedio: Variables (III)

MetaTrader 5Ejemplos | 20 diciembre 2024, 11:51
151 0
CODE X
CODE X

Introducción

El contenido expuesto aquí tiene un único objetivo didáctico. En ningún caso debe considerarse una aplicación final, es decir, no debe utilizarse con el propósito de estudiar los conceptos presentados aquí.

En el artículo anterior "Del básico al intermedio: Variables (II)", hablamos y explicamos cómo utilizar variables estáticas en tus códigos. Esto tiene como objetivo evitar el uso innecesario de variables globales. Muy bien, sobre las variables, al menos lo básico, hemos llegado a su final. Sin embargo, aún existe la cuestión del tipo de datos que puede contener cada variable, lo cual sigue estando de alguna manera relacionado con este tema. No trataré este aspecto dentro del tema de las variables. Lo abordaremos en otro tema dedicado exclusivamente a este aspecto.

Pero si ya hemos hablado de las variables locales y globales, de cómo y por qué declarar una variable como constante e incluso de cómo utilizar variables estáticas, ¿qué más queda por decir sobre este tema? Bien, querido lector, a pesar de que muchos no lo consideran así, existe un tipo muy especial de variable que, en muchos casos, puede considerarse una constante. Sin embargo, sigue siendo un tipo especial de variable. En este caso, me refiero a las funciones. Y, efectivamente, una función es un tipo especial de variable. Aunque, en principio, muchos programadores no lo vean así.

Sin embargo, para que entiendas por qué digo que una función es una variable especial, iniciaremos un nuevo tema.


Una variable especial: Las funciones

Cuando se habla de funciones, lo primero que se les viene a la mente a las personas con conocimientos básicos de programación es el uso de una llamada adicional para ejecutar algún tipo de rutina en nuestra aplicación. Sin embargo, esta definición no es del todo adecuada. Esto se debe a que existen rutinas que no son funciones, sino procedimientos. Existe una pequeña diferencia conceptual entre ambos tipos. La principal diferencia es que una función devuelve un valor, o más concretamente, un tipo de dato. A diferencia de un procedimiento, que no devuelve ningún tipo de dato después de ejecutarse. No obstante, ambos tipos de rutinas pueden modificar y devolver valores diferentes a través de sus argumentos.

Pero no te preocupes ni te rompas la cabeza con esto en este momento. Solo estoy dando una breve descripción de lo que será tratado en más detalle más adelante. Sin embargo, esta pequeña distinción es necesaria en este punto para que puedas entender por qué digo que una función es un tipo especial de variable.

Aquí es importante establecer un paralelismo entre diferentes lenguajes. Esto se debe a que, dependiendo del lenguaje que quieras estudiar en el futuro, el concepto que veremos aquí podría no ser aplicable. Esto se debe precisamente a la forma en que cada lenguaje está implementado para realizar sus tareas.

Normalmente, los lenguajes de script como JavaScript y Python implementan las funciones de tal forma que son simplemente un tipo de variable constante. Sin embargo, en C y C++, una función puede ser tanto una variable constante como una variable que permite modificar el valor de alguna variable estática presente en la función, sin necesidad de pasar ningún argumento. Para muchos, esto puede sonar extremadamente extraño e inquietante, ya que aparentemente este comportamiento no debería ocurrir. No obstante, en C y C++ existe una entidad llamada puntero. Si esta entidad se utiliza para devolver una referencia a una variable estática presente en la función, el código que use esa función podrá modificar su valor.

Esta posibilidad convierte la programación en C y C++ en una verdadera aventura. Aunque en muchos casos puede resultar peligroso, esta característica ofrece una gran libertad al programador. Sin embargo, en MQL5 puro nos encontramos en un término medio entre la forma en que las funciones trabajan en JavaScript y Python, y algo cercano a las posibilidades presentes en C y C++. Aunque no contamos con un nivel de libertad tan amplio, este equilibrio nos brinda un mayor grado de seguridad durante la codificación.

De acuerdo. Para que no nos quedemos únicamente en la parte teórica, que puede resultar muy tediosa, veamos algunos ejemplos simples de funciones en las que podemos utilizarlas como un tipo de variable constante. Un ejemplo bastante típico sería utilizarlas como una especie de definición de valor. Solo que, en lugar de usar directamente el valor, empleamos un nombre más adecuado y representativo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(One_Radian());
07. }
08. //+------------------------------------------------------------------+
09. double One_Radian()
10. {
11.     return 180. / M_PI;
12. }
13. //+------------------------------------------------------------------+

Código 01

Aquí tienes un ejemplo sencillo en el código 01 para que puedas comprenderlo fácilmente. Cuando se ejecute este código, se obtendrá el resultado que se muestra a continuación.


Imagen 01

Bien. Ahora mismo podrías estar preguntándote: ¿por qué hacerlo de esta manera? ¿No sería más fácil colocar el cálculo de la línea 11 directamente en la línea 06 e imprimir el resultado? Sí, lo sería. Sin embargo, si has pensado eso, significa que no has comprendido la idea que quiero transmitir con este ejemplo. La idea no es ejecutar el cálculo propiamente dicho. La idea es generar una constante que pueda utilizarse de manera global en cualquier parte del código. Si cada vez que necesitas saber el valor de una constante tuvieras que escribir el código para calcularla, te resultaría muy difícil mejorar o corregir el código. En cambio, si todo está contenido dentro de una función con un nombre representativo que genere dicha constante, todo será mucho más sencillo, rápido y práctico. Esto te hará mucho más eficiente como programador.

Piensa en la posibilidad de que estés desarrollando un programa 3D que necesite el valor en grados radianes. Y no solo en una línea del código, sino en cientos de líneas distribuidas por todo el programa. En este punto, te pregunto: ¿qué es más fácil, escribir el cálculo cada vez o colocarlo todo en una función, como se muestra en el código 01?

Bien, esta sería una aplicación. No obstante, si te pareció trivial o sin mucho propósito, ¿qué te parece si creamos algo un poco más elaborado con los conocimientos que ya tenemos de los artículos anteriores? Esto se puede hacer como se muestra a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Counting();
07.     Print(Counting());
08.     Counting();
09.     Counting();
10.     Print(Counting());
11. }
12. //+------------------------------------------------------------------+
13. ulong Counting(void)
14. {
15.     static ulong Tic_Tac = 0;
16. 
17.     Tic_Tac = Tic_Tac + 1;
18. 
19.     return Tic_Tac;
20. }
21. //+------------------------------------------------------------------+

Código 02

En este caso, cuando se ejecute este código 02, se obtendrá un resultado similar al que se muestra a continuación.


Imagen 02

Es decir, si el primer ejemplo no te pareció muy adecuado, es posible que este segundo sí lo sea. Esto se debe a que, en muchas ocasiones, necesitamos contar cuántas veces ocurre algo en una aplicación. Muchos programadores recurrirían a una variable global para contabilizar dichos eventos. Sin embargo, como se discutió en los artículos anteriores, las variables globales tienen ciertas desventajas. No obstante, al utilizar una función con un propósito similar, eliminamos los inconvenientes asociados con las variables globales y, al mismo tiempo, garantizamos una mayor estabilidad y seguridad en nuestro código. Además, por supuesto, nos permite modificar las reglas de forma sencilla, rápida y eficiente.


Variables predefinidas

Además de las cuestiones que ya hemos tratado sobre variables y constantes, existen otras en MQL5 que también es necesario mencionar. Esto se debe a que forman parte de un programa que busca aprovechar a fondo los recursos del lenguaje de programación.

A continuación, veremos este tipo especial de variables, ya que es importante que entiendas su existencia y que puedes utilizarlas para determinadas tareas.

En la documentación puedes encontrar más detalles sobre ellas: Variables predefinidas. Sin embargo, aquí no hablaremos de ninguna en concreto. Lo que debemos entender es que, aunque su nombre indique lo contrario, no se trata de variables como las que se utilizan comúnmente. En nuestro código, se tratan como constantes. No obstante, para MetaTrader 5, son efectivamente variables. Sin embargo, aunque para nuestro código se comporten como constantes, en algunos casos es posible usar alguna función o procedimiento del lenguaje MQL5 para modificar el valor de dichas variables.

Es muy importante que comprendas esto, querido lector. Si intentas modificar directamente el valor de una de estas variables, cometerás un error y tu código no se compilará. Cuando digo «intentar modificar directamente el valor de la variable», me refiero a hacer algo como lo que se muestra a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(_LastError);
07.     _LastError = 2;
08.     Print(_LastError);
09. }
10. //+------------------------------------------------------------------+

Código 03

Si estás empezando en MQL5, presta atención a lo que voy a explicar. En la línea 6 de este código 03, estamos solicitando que se imprima en el terminal el valor actual de la variable _LastError. Como esta es una variable predefinida en MQL5, no es necesario declararla. El compilador es perfectamente capaz de reconocer su existencia.

Sin embargo, en el momento en que el compilador encuentre la línea siete, como se ve en este código 03, se producirá un error, como puedes observar en la imagen siguiente.

Figura 03

Pero, ¿por qué sucede esto? El motivo es que estas variables predefinidas en MQL5 se tratan como constantes en el código que estás intentando utilizar. Sin embargo, a nivel del código que será interpretado por MetaTrader 5 durante la fase de ejecución, estas mismas variables no se consideran constantes.

Este tipo de situación puede parecer muy confusa y complicada de entender a primera vista, especialmente para quienes están comenzando en la programación. Sin embargo, antes de adentrarnos en otras cuestiones sobre estas variables predefinidas, querido lector, debes entender lo siguiente: como estas variables existirán en cualquier código de MQL5, NO DEBES intentar crear otra variable con el mismo nombre que estas variables predefinidas. Al intentar hacerlo, también estarás violando la propia seguridad impuesta por la plataforma.

Sin embargo, aunque acceder directamente a estas variables, como se vio en el código 03, constituye un error, el propio lenguaje MQL5 nos proporciona medios para ajustar algunas de estas variables predefinidas. Esto se hace con fines y propósitos específicos. No existe una regla que establezca que estas variables deben definirse con un valor concreto. No obstante, cualquier cambio debe estar justificado. No deberías modificar el valor de estas variables simplemente porque es posible hacerlo, a menos que estés estudiando el lenguaje en sí, como estamos haciendo aquí.

Por ejemplo, si usamos la función SetUserError, podemos establecer un valor para la variable predefinida _LastError. Pero no cualquier valor. El valor que logremos asignar estará restringido, ya que existe una lista de valores de error reservados para el uso del propio MQL5. Esto puede parecer un tanto desalentador para muchos, ya que consideran este tipo de restricciones como una limitación de lo que se puede hacer. Sin embargo, no es exactamente así.

Para comprobarlo en la práctica, puedes utilizar el código que se muestra a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(_LastError);
07.     SetUserError(2);
08.     Print(_LastError);
09. }
10. //+------------------------------------------------------------------+

Código 04

De hecho, este código se compilará y podrá ejecutarse. Sin embargo, cuando lo ejecutes en MetaTrader 5, verás algo similar a lo que se muestra en la siguiente imagen.


Imagen 04

¡Vaya! ¿Pero qué es este número tan extraño? Espera un momento. Es cierto que en la línea siete del código 04 estamos asignando el mismo valor que en la misma línea del código 03. De acuerdo, entendemos que el código 03 no se compiló por los motivos que ya explicamos. Pero aun así, el valor que aparece en la imagen 4 no es el que esperaba.

De hecho, querido lector, este valor es el que intentaste colocar en la variable _LastError, pero fue desplazado. Esto sucede para evitar conflictos con algún valor de error predefinido. Para recuperar el valor que deseabas asignar a _LastError, es necesario realizar un pequeño ajuste. Sin embargo, antes de abordar ese ajuste, es necesario entender algo más. Normalmente, a muchos programadores no les resulta común usar directamente el nombre de una variable predefinida en el código. Muchos prefieren, y de hecho es lo más recomendable, utilizar una función que nos devuelva el valor de dicha variable predefinida. Aunque nada te impide escribir el código como se mostró anteriormente, lo más común y, para muchos, lo más correcto, sería emplear un código como el siguiente:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(GetLastError());
07.     SetUserError(2);
08.     Print(GetLastError());
09. }
10. //+------------------------------------------------------------------+

Código 05

Nota que, básicamente, el código hace lo mismo. Sin embargo, para muchos, este código 05 es más legible que el código 04. Esto se debe a que dejamos claro a otros programadores que puedan leer el código que estamos accediendo al valor de una variable predefinida. ¿Has observado cómo lo explicado en el tema anterior también se aplica aquí? La forma en que se accede a las variables predefinidas para leerlas no es importante. Pero el método para modificar sus valores siempre será el mismo. Por ejemplo, para eliminar cualquier valor presente en la variable _LastError, NO DEBES usar el procedimiento mostrado en la línea siete de los códigos anteriores. Si lo haces, estarás asignando un nuevo valor de error. La manera correcta de limpiar o eliminar cualquier error contenido en _LastError es utilizar otro procedimiento proporcionado por MQL5. Este procedimiento se muestra a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(GetLastError());
07.     SetUserError(2);
08.     Print(GetLastError());
09.     SetUserError(0);
10.     Print(GetLastError());
11.     ResetLastError();
12.     Print(GetLastError());
13. }
14. //+------------------------------------------------------------------+

Código 06

Si es la primera vez que lo ves, querido lector, presta atención. Cuando se ejecute este código, el resultado será el que se muestra a continuación.


Imagen 05

Observa lo siguiente: cuando la línea seis se ejecute, se imprimirá la primera línea que ves en la imagen 05. Esto indica que no hay ningún error o valor en la variable _LastError. Una vez que la línea siete se ejecute, asignaremos un valor a la variable _LastError, como se vio anteriormente. Sin embargo, muchos principiantes intentan utilizar la línea nueve para limpiar la variable _LastError. Pero, al hacerlo e imprimir el resultado, obtendremos un valor distinto de cero. ¿Por qué sucede esto? La razón es la misma que mencioné antes. Cuando utilizamos SetUserError para asignar un valor a _LastError, este valor es desplazado. Por esta razón, intentar asignar cualquier valor usando esta función no dará como resultado el valor esperado. Ahora bien, como nuestro objetivo aquí es colocar el valor cero en la variable _LastError, la manera correcta de hacerlo es usando la línea 11. Cuando se ejecute el procedimiento de la línea 11, el valor cero será efectivamente asignado a _LastError.

Esto debe tenerse en cuenta para todas las demás variables predefinidas. Por supuesto, no mostraré aquí uno por uno los procedimientos involucrados, ya que basta con consultar la documentación para entender cuáles son los procedimientos correctos para asignar valores a estas variables, si los hay. Sin embargo, la lectura de estos valores puede realizarse directamente, como se mostró al inicio del tema. Es decir, consultando directamente el nombre de la variable predefinida.

Pero espera un momento. He logrado seguir y entender lo que se explicó aquí. Sin embargo, aún no comprendo el tema del desplazamiento de valor que mencionaste varias veces durante la explicación. ¿Podrías explicarlo mejor? Tal vez así pueda usar los valores que asigné a la variable _LastError en lugar de esos valores extraños que aparecen en el terminal al imprimir el valor de la variable.

De hecho, esta es una solicitud legítima que merece una explicación. Por lo tanto, crearemos un nuevo tema para abordarla, ya que también podremos tratar otros aspectos relacionados.


Enumeraciones y definiciones de constantes

Uno de los temas que, en mi opinión, es más difícil de definir, independientemente del lenguaje de programación, es la existencia de definiciones y enumeraciones dentro del lenguaje. Y no me malinterpretes, querido lector. Ambas son muy utilizadas y son de gran ayuda durante la programación. Sin embargo, es difícil definirlas solo observando la documentación, para determinar qué sería una enumeración y qué sería una definición. Esto se debe a que no hay forma de saberlo a menos que la documentación especifique qué tipo de modelado se está utilizando.

Sin entrar aún en detalles sobre estas cuestiones, ya que lo haremos en otro momento, podemos empezar por entender qué representa cada valor. Pero, sobre todo, debemos comprender algunas cosas que suelen aparecer en los códigos, como textos que, a primera vista, no tienen mucho sentido. Un ejemplo de esto se puede ver en el código siguiente.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. int OnInit()
05. {
06.    return INIT_SUCCEEDED;
07. };
08. //+------------------------------------------------------------------+
09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
10. {
11.    return rates_total;
12. };
13. //+------------------------------------------------------------------+

Código 07

Aquí, en la línea seis, encontramos algo que, para muchos, no tiene mucho sentido, aunque aparece en prácticamente todo código de indicador. Hablaremos más sobre esto en su momento, querido lector. Pero estoy mostrando este código 07 solo para ejemplificar algo que encontrarás en muchos códigos. Aunque la palabra reservada return espera una variable o un valor, aquí estamos utilizando un texto. ¿Qué significa este texto y por qué usamos este y no otro? En realidad, podríamos usar algo diferente. Pero vayamos paso a paso.

Si consultas la sección de Constantes, Enumeradores y Estructuras, verás que existen muchas constantes y enumeraciones definidas en MQL5. Estas enumeraciones y constantes, como la que aparece en la línea seis del código 07, reciben un nombre en programación. A estas entidades se les llama alias. Y sí, se escriben exactamente así, ya que no podemos usar acentos en un lenguaje de programación. Su propósito es crear una representación más adecuada que nos ayude a recordar su función o a facilitar la lectura del código, tanto a quien lo creó como a otros programadores.

Para que esto quede realmente claro, piensa en lo siguiente, querido lector. Supongamos que estás programando algo y que, para indicar que tu código ha tenido éxito al realizar una operación, deseas devolver el valor verdadero, o true, como se conoce en programación. ¿Cómo lo harías?

Bien, podrías usar un valor mayor que cero o escribir simplemente la palabra true. Ambos funcionarían, EN TEORÍA. Es decir, ese mismo valor en la línea 6 del código 07 podría haberse escrito como se muestra a continuación.

//+------------------------------------------------------------------+
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
int OnInit()
{
   return true;
};
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
   return rates_total;
};
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    Print(reason);
};
//+------------------------------------------------------------------+

Código 08

Sin embargo, al hacerlo, tu código fallará. ¿Por qué fallará? Esto parece no tener ningún sentido, ya que al usar el valor true, estamos diciendo que OnInit se completó con éxito. Sí, querido lector. Al devolver true, estás afirmando que algo se ejecutó sin errores. Sin embargo, incluso en ese caso, tu código fallará y se cerrará inmediatamente después de que OnInit haya sido ejecutado. La razón detrás de esto radica en el valor asociado al alias INIT_SUCCEEDED.

No es que no podamos usar otro valor, simplemente necesitas entender que INIT_SUCCEEDED está reemplazando un valor de manera más legible para los humanos. Si consultas la documentación, verás que el valor de INIT_SUCCEEDED es, en realidad, cero. En lógica booleana, el valor cero representa false o falso. Por lo tanto, al usar el valor true o verdadero, el código 08 fallará, aunque no haya ningún error en él.

Un detalle importante: si ejecutas este código 08, verás en el terminal el siguiente mensaje impreso justo después de que se ejecute el código.


Imagen 06

Sé que ahora no tiene ningún sentido, pero te garantizo que con el tiempo todo quedará claro. Por lo tanto, podrías usar el valor cero o false en la línea seis del código 08 y funcionaría perfectamente, siempre y cuando elimines el indicador del gráfico. Sin embargo, no es el único tipo de alias que se puede usar. También podrías utilizar algo como lo que se muestra a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. int OnInit()
05. {
06.    return ERR_SUCCESS;
07. };
08. //+------------------------------------------------------------------+
09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
10. {
11.    return rates_total;
12. };
13. //+------------------------------------------------------------------+
14. void OnDeinit(const int reason)
15. {
16.     Print(reason);
17. };
18. //+------------------------------------------------------------------+

Código 09

En el caso mostrado en el código 09, así como al usar el valor cero o false en la línea seis del código 08, se obtendrá el resultado que se muestra a continuación, una vez que el indicador sea eliminado del gráfico.


Imagen 07

Observa que, en realidad, no hay una regla estricta sobre cómo debe crearse un código. Solo hay principios que debes comprender para que todo funcione correctamente. Supongamos ahora que eres un programador extremadamente riguroso, que no tolera ningún error en sus aplicaciones. En ese caso, podrías utilizar el código que se muestra a continuación en la línea seis.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. int OnInit()
05. {
06.    return GetLastError();
07. };
08. //+------------------------------------------------------------------+
09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
10. {
11.    return rates_total;
12. };
13. //+------------------------------------------------------------------+
14. void OnDeinit(const int reason)
15. {
16.     Print(reason);
17. };
18. //+------------------------------------------------------------------+

Código 10

Este sería un caso extremo, ya que si durante la ejecución de cualquier parte del código presente en OnInit ocurre algún error, el valor retornado será diferente de cero o de alguno de los valores indicados anteriormente que representen el cero. Esto se debe a que GetLastError, como se mencionó antes, detectará que _LastError contiene algún valor. Sin embargo, es importante tener en cuenta que el error podría estar ocurriendo en cualquier parte del código. Y este error podría o no formar parte de lo que tú programaste. A veces, los errores no se deben a fallos en el código, sino a interacciones entre valores. Esto podría provocar que la variable _LastError tuviera un valor diferente de cero por cualquier motivo. Por esta razón, este tipo de manejo es extremo. Es raro encontrar código que utilice este enfoque. Sin embargo, si decides probarlo, te darás cuenta de que suelen ocurrir situaciones curiosas.

Esto te ayudará a ganar experiencia y a mejorar tu entendimiento sobre cómo manejar situaciones inesperadas. Pero hazlo con calma y mucha paciencia, querido lector, porque, como aún no hemos explicado cómo filtrar los eventos, cualquier mínimo detalle podría desorientarte por completo mientras intentas comprender por qué, en algunas ocasiones, una aplicación funciona y, en otras, deja de funcionar sin un motivo aparente.

Muy bien, como última cuestión que trataremos y mostraremos en este artículo, veremos cómo el uso de las constantes y enumeraciones definidas internamente por MQL5 puede influir en nuestra labor. Como se mostró en el tema anterior, al usar el procedimiento SetUserError podemos asignar cualquier valor a la variable de sistema _LastError. Pero, ¿y si deseamos saber exactamente cuál fue el valor asignado con SetUserError? ¿Cómo podemos averiguarlo? Esta es la parte sencilla, querido lector. En la documentación de SetUserError se describe cómo debe realizarse el ajuste. A continuación, se incluye parte del texto:

Define la variable predefinida _LastError con el valor igual a ERR_USER_ERROR_FIRST + user_error

Es decir, si restamos ERR_USER_ERROR_FIRST del valor encontrado en _LastError, podremos determinar el valor exacto que se definió en SetUserError. Veamos un ejemplo de aplicación en el código a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(GetLastError());
07.     SetUserError(2);
08.     Print(GetLastError());
09.     Print(GetLastError() - ERR_USER_ERROR_FIRST);
10. }
11. //+------------------------------------------------------------------+

Código 11

Al ejecutar este código 11, podrás visualizar en el terminal lo que se muestra en la imagen a continuación.


Imagen 08

Observa que se imprimen tres valores. Sin embargo, los que nos interesan son el segundo y el tercero, ya que en este caso se está realizando precisamente la corrección del valor para identificar cuál fue el valor exacto asignado mediante el procedimiento SetUserError, con el fin de indicar un error.


Consideraciones finales

Hay una pequeña observación que debe hacerse sobre lo que se ha mostrado aquí. Es muy probable que pienses que, si utilizas un valor negativo en SetUserError, podrás entrar dentro del rango de los valores de error definidos por MQL5 para reportar algo especial. Sin embargo, los valores negativos NO tendrán ningún efecto. Esto se debe al tipo de dato que espera el procedimiento SetUserError.

Precisamente este tipo de situaciones serán abordadas y explicadas en el próximo artículo. En cuanto a los códigos mostrados aquí, en el anexo tendrás acceso a una parte de ellos. Así podrás estudiar con calma todo lo presentado en este artículo. Así que, ¡buenos estudios y nos vemos en el próximo artículo!

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

Archivos adjuntos |
Anexo.zip (2.11 KB)
Del básico al intermedio: Operadores Del básico al intermedio: Operadores
En este artículo, exploraremos los operadores básicos. Aunque es un tema fácil de comprender, existen pequeños detalles que marcan una gran diferencia a la hora de incorporar expresiones matemáticas en formato de código. Sin comprender adecuadamente estos detalles, muchos programadores con poca o ninguna experiencia terminan abandonando su intento de crear sus propias soluciones.
Indicador personalizado: Trazado de puntos de entradas parciales en cuentas netting Indicador personalizado: Trazado de puntos de entradas parciales en cuentas netting
En este artículo, exploraremos una forma interesante y diferente de crear un indicador en MQL5. En lugar de centrarnos en una tendencia o patrón gráfico, el objetivo será gestionar nuestras propias posiciones, incluyendo las entradas y salidas parciales. Utilizaremos intensivamente matrices dinámicas y algunas funciones comerciales (Trade) relacionadas con el historial de transacciones y las posiciones abiertas para indicar en el gráfico dónde se llevaron a cabo estas operaciones.
Redes neuronales: así de sencillo (Parte 96): Extracción multinivel de características (MSFformer) Redes neuronales: así de sencillo (Parte 96): Extracción multinivel de características (MSFformer)
Extraer y combinar eficazmente las dependencias a largo plazo y las características a corto plazo sigue siendo una tarea importante en el análisis de series temporales. Para crear modelos predictivos precisos y fiables deberemos comprender e integrar estos adecuadamente.
Desarrollo de un Asesor Experto (EA) en MQL5 basado en la estrategia de ruptura del rango de consolidación Desarrollo de un Asesor Experto (EA) en MQL5 basado en la estrategia de ruptura del rango de consolidación
Este artículo describe los pasos para crear un Asesor Experto (EA) que aproveche las rupturas de precios después de los períodos de consolidación. Al identificar rangos de consolidación y establecer niveles de ruptura, los operadores pueden automatizar sus decisiones comerciales basándose en esta estrategia. El Asesor Experto tiene como objetivo proporcionar puntos de entrada y salida claros y evitar rupturas falsas.