Bucles de tipo while
Las estructuras de tipo while pueden sernos útiles en muchas ocasiones. Su forma general es:
while(condición)
{ código a ejecutar mientras se dé la condición }
La principal diferencia de este tipo de estructura con los bucles de tipo for es que no existe una variable contador. Por tanto, si no necesitamos un contador podemos ahorrarnos trabajar con for y usar una bucle while. A medida que avancemos en la serie iremos viendo ejemplos de este tipo de estructura.
Un nuevo tipo de condiciones. El operador switch.
Con el operador switch, podemos ahorrarnos complejas estructuras compuestas por varios if y else. Fundamentalmente la estructura de tipo switch es utilizada cuando necesitamos realizar una tarea en función del valor de una variable. Por ejemplo, supongamos que deseamos crear un sistema cuyo comportamiento varía en función de la situación en qué esté el mercado. Así, declaramos la variable que toma valores enteros, marketState:
int marketState
Que puede tomar los siguientes valores:
1, si el mercado es alcista
2, si el mercado es bajista
3, si el mercado está sin tendencia
Si ahora creáramos el sistema combinando varios if y else tendríamos que:
if (marketState == 1)
{ // Estrategia Mercado Alcista }
else if(marketState == 2)
{ // Estrategia Mercado Bajista }
else if(marketState == 3)
{ // Estrategia Mercado sin Tendencia
else { // Error: no se consideran otras posibilidades }
Como podemos ver, todas las condiciones dependen de una única variable y en todas se compara su valor con un significado asociado a él. En este tipo de situaciones, las estructuras de tipo switch son mucho más eficaces:
switch(marketState)
{
case 1: // Estrategia Mercado Alcista
break;
case 2: // Estrategia Mercado Bajista
break;
case 3: // Estrategia Mercado sin Tendencia
break;
default: // Error: no se consideran otras posibilidades
break;
}
Obsérvese en el ejemplo anterior que hemos definido previamente la variable de estado marketState.
En general, las estructuras de tipo switch presentan la siguiente forma:
switch(variable de estado)
{ case [valor de la variable]: // Código a ejecutar para este caso
break;
case [otro valor de la variable]: // Código a ejecutar para este caso
break;
default: // Código para el resto de casos
break; }
Más operadores: continue y break
Acabamos de ver en la estructura switch el uso del operador break. Pero break puede utilizarse no sólo dentro de una estructura de ese tipo sino en general para salir de cualquier tipo de bucle. Por ejemplo, supongamos que necesitamos saber cuál es la última barra hasta la que el volumen negociado acumulado ha sido mayor o igual a 1000. Para ello podemos utilizar el siguiente código:
int a = 0;
double volume = 0.0;
while(a < Bars)
{
// Si la variable volume supera 1000 points, salimos del bucle while
if(volume > 1000.0)
break;
volume = volume + Volume[a];
// Alternativamente para acumular valores en una variable también
// podemos escribir también volume += Volume[a];
a++; }
El otro operador relacionado con este tipo de tareas es continue, el cual se utiliza para omitir iteraciones no deseadas. Supongamos que queremos obtener el volumen total de las barras mostradas en el gráfico pero ignorando aquellas barras cuyo volumen sea superior a 50. Para ello, utilizaríamos el siguiente código:
int a = -1;
double volume = 0.0;
while(a < Bars)
{ a++;
// Si el volumen supera el valor 50, omitimos esa barra
if(Volume[a] > 50.0)
continue;
volume += Volume[a]; }
Escribiendo nuestras propias funciones
Normalmente nos encontraremos con situaciones en las que veremos que se repite el código varias veces. Para ahorrarnos tiempo tecleando podemos crearnos funciones para llamarlas en los momentos que las necesitemos. Veamos un ejemplo para entenderlo.
Supongamos que deseamos saber el color de la última vela aparecida en el gráfico. Para ello, declaramos una variable booleana (tan sólo hay dos casos posibles, blanco o negro) tal que:
bool color;
// Suponemos que color = false se corresponde con una vela negra
if(Close[0] > Open[0])
color = true;
if(Open[0] > Close[0])
color = false;
Si quisieramos saber el color de la vela en otra región del gráfico (por ejemplo, hace 5 velas), deberíamos reemplazar [0] por el valor correspondiente. Pero qué pasaría si necesitáramos hacerlo para todas las velas del gráfico? Es en este tipo de situaciones en las que necesitamos recurrir a funciones. Vamos a crear una función que nos permitirá determinar el color de cualquier vela:
bool color;
color = GetColor(0);
Como podemos ver, en lugar de imponer la condición para obtener el valor de la variable color, lo que hacemos es crear una función que nos devolverá el valor (true/false – blanco/negro) y que se llamará GetColor. Obsérvese que posee un argumento (el cero entre paréntesis) hace referencia a la última barra del gráfico.
La estructura general de una función en MQL4 es la siguiente:
[tipo de valor devuelto] [nombre de la función] ([lista de argumentos])
{ // código de la función
return([valor a devolver]); }
Veamos cómo escribiríamos la función GetColor:
bool GetColor(int index)
{ bool color;
if(Close[index] > Open[index])
color = true;
if(Open[index] > Close[index])
color = false;
return(color); }
Seguramente les llame la atención la primera línea, en la que aparece int index entre paréntesis. Sin embargo, cuando llamamos a la función, habíamos escrito simplemente GetColor(0). Qué es lo que ocurre? Sencillamente que cuando hemos escrito la función, hemos definido con la variable index el tipo de argumento que recibirá la función (en este caso de tipo int – Entero).
Por supuesto, si necesitaramos más argumentos porque estuviéramos escribiendo una función más compleja, simplemente bastaría con introducirlos separados por comas. Por ejemplo:
bool FuncionComplicada(int Argumento1, int Argumento2, string Argumentostring)
Si quisieramos que la función no devuelva ningún valor utilizaremos el comando void:
void function() { // code }
También podemos establecer valores por defecto para los argumentos de la función. Por ejemplo:
void FuncionA(int argumento1, int argumento2, int argumentoespecial = 50)
{ // code }
Si tuvieramos que llamar a la función FuncionA, no sería necesario pasarle un valor al tercer argumento (argumento especial) ya que tiene asignado un valor igual a 50 por defecto.
En todo caso, debemos recordar que los argumentos que tengan valores asignados por defecto deben aparecer siempre al final de la lista de argumentos.
Arrays multidimensionales
En MQL4 también podemos trabajar con arrays de varias dimensiones, es decir, matrices de datos. Un ejemplo de array multidimensional sería el siguiente:
[1] 40 50 60
[2] 70 80 90
[0] [1] [2]
Los números que aparecen entre corchetes indican cómo son los índices utilizados para referenciar filas y columnas en MQL4. Así, el elemento [0] [1] sería 20, el elemento [1] [2] sería 60 y así sucesivamente.
Para crear una matriz como la anterior en MQL4, tenemos el comando array2D[num filas] [num columnas] tal que:
int array2D[3][3]=
{10,20,30,
40,50,60,
70,80,90};
También podemos crear arrays en 3D mediante el siguiente comando:
int array3D[num x][num y][num z] = {lista de números separada por comas};
Algunas funciones adicionales para trabajar con arrays serían las siguientes:
- Obtener el número de elementos de un array: int ArraySize(objeto array[]);
- Asignar valores a un array: int ArrayInitialize(objeto array[],double valor);
- Valor máximo y mínimo de un array:
int ArrayMaximum(double array[], int count = WHOLE_ARRAY, int start = 0);
int ArrayMinimum(double array[], int count = WHOLE_ARRAY, int start = 0);
- Calcular la dimensión de un array: int ArrayDimension(objeto array[]);
- Ordenar los elementos de un array:
int ArraySort(double&array[], int count = WHOLE_ARRAY, int start = 0, int sort_dir = MODE_ASCEND);
- Copiar un array dentro de otro:
int ArrayCopy(object&dest[], object source[], int start_dest = 0, int start_source=0, int count=WHOLE_ARRAY);
Preprocesadores
Terminamos la entrega de hoy con lo que se conoce como preprocesador. Veamos en qué consisten con un sencillo ejemplo. Hace algunos párrafos veíamos la siguiente estructura de tipo switch:
switch(marketState)
{
case 1: // Estrategia Mercado Alcista
break;
case 2: // Estrategia Mercado Bajista
break;
case 3: // Estrategia Mercado sin Tendencia
break;
default: // Error: no se consideran otras posibilidades
break;
}
Qué les parecería poder cambiar los valores 1, 2 y 3 por las palabras Alcista, Bajista y Plano? Realmente el código sería aún más comprensible, verdad?
Pues bien, para poder hacer eso simplemente basta con definir preprocesadores de la siguiente forma: al comienzo del código, insertaríamos las siguientes líneas:
#define ALCISTA 1
#define BAJISTA 2
#define PLANO 3
Como podemos observar, los preprocesadores siempre van precedidos del símbolo #.
Mediante este código lo que hacemos es que, por ejemplo, cada vez que aparezca la palabra ALCISTA en el código a compilar, ésta será reemplazado por el valor 1.
Para comprobarlo, creen un nuevo script e introduzcan el siguiente código para luego ejecutarlo:
//+——————————————————————+
//| Preprocesador.mq4 |
//| X-Trader.net |
//| |
//+——————————————————————+
#property copyright «X-Trader.net»
#property link «https://www.x-trader.net»
#define ALCISTA 1
#define BAJISTA 2
#define PLANO 3
//+——————————————————————+
//| script program start function |
//+——————————————————————+
int start()
{
MessageBox(«ALCISTA=» + ALCISTA + » BAJISTA=» + BAJISTA +
» PLANO=» + PLANO);
return(0);
}
Un saludo,
X-Trader